1 Introduction

As of August 2020, over 75 countries had implemented DHIS2 Tracker, an open source tool designed to capture indivudal level health data. Those deployed support a wide spectrum of health programs–and layer on additional client-communication and decision support features–but within the realm of digital health interventions, DHIS2 Tracker often falls under the broad WHO classification of a “Client Health Record” which supports “Longitudinal tracking of clients’ health status and services”.

A central feature of Client Health Records is a single cohesive record for each client, which can be shared and accessed across various levels of the health system. The portability of a health record is a significant, life-saving innovation. As soon as the patient is provided a unique identifier code, a care provider in the hospital might, for example, transmit limited critical test results to a community health worker’s client register, and vice versa.

Not only does a shared health record strengthen continuity of care and enhanced decision support systems, but enable analysis of client movement through the health system. Health system managers can access real-time information on referrals or care-seeking behavior; lag times between lab result notification and outreach by a frontline care worker; identify clinics that “leak” patients to other clinics, and those that “gain” clients from other locations.

However, DHIS2 has not traditionally supported advanced analysis of such multidimensional individual-level data. Most program indicators of DHIS2 Tracker programs are aggregated by the location of discrete facility visits (“events”) or patient registrations (“enrollments”). Essentially, the organization unit (“orgUnit” or “OU”) can only be used as a single dimension within a chart or pivot table. Absent any designations of a “transfer from” org unit and a “transfer to” org unit, there is limited analysis of patient “cross-over” between facilities. Yet, the raw data is available within DHIS2 to perform a “cross-over” analysis in external software, such as R.

The goal of this paper is to present exploratory visualizations that convey client movement patterns found in DHIS2, and support development of a generic analytic framework to visualize these data as a native DHIS2 feature. This work should be seen as experimental and not prescriptive. Code is provided to encourage reader experimentation and comment. Furthermore, separate research will be conducted to untangle the determinants for patient movement specific to the case study in Bangladesh.

1.1 Visual Encoding and Gestalt Principles

To assess the appropriateness and effectiveness of each data visualization format, this paper will describe the tested visualizations through Gestalt principles and William Cleveland’s channels for mapping data.

Visual Channel Rankings

A number of psychological experiments have been conducted to reveal which information channels are most expressive and accurate when visually perceived. For example, William S. Cleveland and Robert McGill asked participants to estimate the difference between two values within a chart e.g., two bars in a bar chart, or two slices of a pie chart. From the errors recorded, they determine that numeric information is more effectively communicated through the length (bar chart) than angle (pie chart). Their results have been collated into a hierarchy of visual channels to express both ordered and categorical attributes. The rankings are outlined in Tamara Munzer’s textbook “Visual Analysis & Design”, below, and you can read more about Cleveland’s original experiments here.

Fundamentally, this ranking of visual channels will help a designer match the salience of a given data dimension to its prominence within the visualization.

Munzer 2014, fig 5.6, Pg 102

Munzer 2014, fig 5.6, Pg 102

Gestalt Principles

But in visualization, as the saying goes, “the whole is more than the sum of the parts”. The Gestalt Principles, developed by German psychologists in the 1920s, help explain the holistic characteristics of perception in data visualization.

Taken together, Gestalt Principles suggest how users will read and interpret experimental visualizations as a whole, providing a useful design framework drawing draw users’ attention to key parts of a graph. Visual channels summarize how each part of a visualization is perceived; Gestalt Principles assess the whole.

The seven principles are… * Proximity: Things that are spatially near to one another seem to be related. * Similarity: Things that look alike seem to be related. * Connection: Things that are visually tied to one another seem to be related. * Continuity: Partially hidden objects are completed into familiar shapes. * Closure: Incomplete shapes are perceived as complete. * Figure and Ground: Visual elements are taken to be either in the foreground or the background. * Common Fate: Elements sharing a direction of movement are perceived as a unit.

knitr::include_graphics("http://www.scholarpedia.org/w/images/thumb/1/18/Todorovic-Gestalt_principles-Figure_4.jpg/800px-Todorovic-Gestalt_principles-Figure_4.jpg")

Previous research suggests these principles are especially salient when considering interactive or animated visuals, which are less common in the context of DHIS2 or RMNCH.

1.2 The Matlab eRegistry context

Data were collected as part of an ongoing randomized controlled trial in Matlab, Bangladesh. Broadly, the primary research question is whether a comprehensive eRegistries system with decision support and targeted client communication, compared to a control electronic data entry screen without feedback, leads to improved quality of care in maternal health system.

The system was built on top of DHIS2 Tracker, and run on both Android devices and chromebooks. It presents an ideal use case to examine Org Unit cross-over, for two key reasons.

First, the system employs a novel approach to a main challenge of client health records in in low-resource settings: unique client identification. For the purposes of the trial, 99.9% of clients consented to identification with a palm-based biometric system developed by Element, Inc., which leverages the built-in camera on mobile devices to image palmprints and generate unique identifiers that are copied into the DHIS2 app. The application runs on the same mobile devices used to enter data, and also has the ability to work offline in areas with poor connectivity. In separate trials on a subset of 150 clients in Matlab, 84% of care providers were able to correctly identify a patient with the app on the first scan; 100% were identified within 3 attempts.

While duplicate records may still be possible within the database, duplicates are much less common than other community-level client health records, due to an ultra-portable, highly accurate biometric ID system.

The tweet below illustrates this workflow in practice.

tweetrmd::tweet_embed("https://twitter.com/eRegistries/status/1064585334216749057")
#knitr::include_graphics("https://pbs.twimg.com/media/DsYpHaMVYAAjCsY?format=jpg&name=small")

Second, the system encourages sharing of records across org units. Indeed, the link between community-level identification and facility-level service delivery is built into the standard workflow. As designed, a community health worker can identify a pregnancy in first trimester, then encourage the client to seek clinical care. When the later opens the client record, she already has the gestational age calculated by the community health worker. So, if a high blood pressure reading is entered, the diagnosis would be automatically written to the record as either chronic or gestational hypertension, dependent on trimester of visit. The diagnoses would then be visible to the community worker at the next visit.

1.2.1 Note on Org Unit Hierarchy of MCH Services in Bangladesh

For our purposes it is important to know which workers and organiszation units (aka “org units” or “OUs”) provide reproductive health services in Matlab. There are two directorates under the Bangladesh Ministry of Health and Family Welfare that provide services, with cadres of community and facility based health workers. In particular, Family Welfare Assistants are responsible for Family Planning services to their FWA Units. They routinely visit all households in the area with women of reproductive age to counsel, share contraception options, and offer pregnancy tests. Health Assistants have community outposts to provide vaccination services at Ward level. Any of these health workers, plus the facility based staff at Community Clinics and Family Welfare Centres, can identify a pregnancy within the e-Registry system, on chromebooks or tablets provided by the project.

ou_table<-tibble("Directorate General of Family Planning"=c("Family Welfare Center (FWC)","Family Welfare Assistant (FWA) Unit"),
                 "Directorate General of Health Services"=c("Community Centre (CC)","Health Assistant (HA) Ward"))

rownames(ou_table)<-c("Facility","Community")

kable(ou_table) %>% 
  kable_styling()
Directorate General of Family Planning Directorate General of Health Services
Facility Family Welfare Center (FWC) Community Centre (CC)
Community Family Welfare Assistant (FWA) Unit Health Assistant (HA) Ward

2 Data Import and Processing

These analyses pull data from production and development DHIS2 environments to assess client movement.

Raw data were pulled from production through SQL view by the implementing partner, and look like this:

#read file from production and view

raw4<-suppressMessages(read_csv("BrianDs.csv"))

#View(raw4)
#colnames(raw4)

head(raw4)
## # A tibble: 6 x 8
##   EnrOrgUnit  ENrUserType TeiUid StageUid VisitNo EvtOrgUnit EvtUserType GestAge
##   <chr>       <chr>       <chr>  <chr>      <dbl> <chr>      <chr>       <chr>  
## 1 uyaWnaBmAs1 HA          rbodU~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.3   
## 2 uyaWnaBmAs1 HA          kkpNN~ Ty22Qt2~       1 uyaWnaBmA~ HA          13.4   
## 3 uyaWnaBmAs1 HA          v14CD~ Ty22Qt2~       1 uyaWnaBmA~ HA          19.4   
## 4 uyaWnaBmAs1 HA          vd7bT~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.1   
## 5 uyaWnaBmAs1 HA          trPZE~ Ty22Qt2~       1 uyaWnaBmA~ HA          25.3   
## 6 gtUM8Eraqvu HA          TdT1e~ Ty22Qt2~       1 gtUM8Eraq~ HA          21.4

To provide context, metadata from development environment

#now get background data from dev
#get Org Unit groups
baseurl<-"https://bd-eregistry.dhis2.org/dhis/"
username<-"ing_test"


#function for logging in
loginDHIS2<-function(baseurl,username,password) {
  url<-paste0(baseurl,"api/me")
  r<-GET(url,authenticate(username,password))
  warn_for_status(r, task="log in")
  if(r$status_code == 200L){return(TRUE)}
}


if(loginDHIS2(baseurl, username, password)==TRUE){
  print("successfully logged in")
}else{
  stop("could not log in! Please check url, username and password")
}
## [1] "successfully logged in"
#groups
url<-paste0(baseurl, "api/organisationUnitGroups.json?paging=false&fields=id,name,organisationUnits")
ou_groups<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("name"=1,"id"=2,"members"=3) %>% 
  unnest_longer(members) %>% 
  flatten()
head(ou_groups)
##                                 name          id  members.id
## 1 10/20/31/50 Bed Hospital (not UHC) iXQnUg4Ayjg        <NA>
## 2         Baganbari Union, Uttar Mat qzxlprBwoN2 TNlLoHITukJ
## 3         Baganbari Union, Uttar Mat qzxlprBwoN2 qDuy7VCWXEu
## 4         Baganbari Union, Uttar Mat qzxlprBwoN2 Law6euHPccf
## 5         Baganbari Union, Uttar Mat qzxlprBwoN2 kKuwT6hXLqh
## 6         Baganbari Union, Uttar Mat qzxlprBwoN2 oKhlabKlBHa
#stages
url<-paste0(baseurl, "api/programStages.json?paging=false&fields=id,name")
ps<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("id"=2, "psname"=1)

tail(ps)
##             id
## 65 WpQ5WTtriMf
## 66 IOUjUFmcfZ3
## 67 xhvIu5AvMOS
## 68 ng2rFaksnqy
## 69 EsFfszdkMKC
## 70 Ozl1A7v4B5X
##                                                                                                                                                                                                                                 psname
## 65                                                                                                            <U+099F><U+09BF><U+099F><U+09BF> <U+099F><U+09BF><U+0995><U+09BE> <U+09B8><U+09C7><U+09AC><U+09BE> (TT Service Delivery)
## 66                                      <U+09AA><U+09CD><U+09B0><U+09B8><U+09AC><U+09AA><U+09B0><U+09AC><U+09B0><U+09CD><U+09A4><U+09C0> <U+09B8><U+09C7><U+09AC><U+09BE><U+09B0> <U+09A4><U+09A5><U+09CD><U+09AF> (Home - PNC record)
## 67                           <U+09AA><U+09CD><U+09B0><U+09B8><U+09AC> <U+09B8><U+09C7><U+09AC><U+09BE><U+09B0> <U+09A4><U+09A5><U+09CD><U+09AF> - <U+09A8><U+09AC><U+099C><U+09BE><U+09A4><U+0995> (Home - Pregnancy Outcome (Newborn)
## 68                                                          <U+09AA><U+09CD><U+09B0><U+09B8><U+09AC> <U+09B8><U+09C7><U+09AC><U+09BE><U+09B0> <U+09A4><U+09A5><U+09CD><U+09AF> - <U+09AE><U+09BE> (Home - Pregnancy Outcome (Maternal)
## 69                                                     <U+09AC><U+09BF> <U+09B8><U+09BF> <U+099C><U+09BF> <U+099F><U+09BF><U+0995><U+09BE> <U+09A6><U+09C7><U+09DF><U+09BE> <U+09B9><U+09DF><U+09C7><U+099B><U+09C7> (BCG Vaccination)
## 70 <U+09AC><U+09BF> <U+09B8><U+09BF> <U+099C><U+09BF> <U+099F><U+09BF><U+0995><U+09BE> <U+09AA><U+09CD><U+09B0><U+09A6><U+09BE><U+09A8> <U+0995><U+09B0><U+09BE> <U+09B9><U+09DF> <U+09A8><U+09BE><U+0987> (BCG Vaccination NOT given)
#OU names
url<-paste0(baseurl, "api/organisationUnits.json?paging=false&fields=id,name")
ou_names<-fromJSON(content(GET(url), "text"), flatten = TRUE) %>% 
  data.frame() %>% 
  select("name"=1, "ou_id"=2) %>% 
  mutate("ou_type"=case_when(
    str_detect(name, " FWC") ~ "FWC",
    str_detect(name, "(?i)CC") ~ "CC",
    str_detect(name, "Unit") ~ "Unit",
    str_detect(name, "Ward") ~ "Ward"))

head(ou_names)
##                               name       ou_id ou_type
## 1                    Aburkandi FWC mf3RMT3QRhf     FWC
## 2 Aithadi Pacani CC, Matlab(North) si7FoHyJpZ0      CC
## 3       Amiapur Cc - Matlab(north) P1okfUePFEd      CC
## 4                     Aswinpur FWC ubVXGg2leZa     FWC
## 5    BAGANBARI UNION, UTTAR MATLAB mR8aUxvBDgQ    <NA>
## 6             BAHERCHOR LOTURDI CC PduJMfKZhFt      CC

2.1 Processing

We then merge the raw outputs of TEI events with org unit information.

When arranging events by visit number, we classify subsequent visits as being at the same or different org unit than the initial visit.

my_data<-raw4 %>% 
  left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>% 
  select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>% 
  distinct() %>% #event must be unique visit, i.e. an ANC management and ANC stage same week would be merged
  mutate(ga=round(as.numeric(ga))) %>% 
  filter(!is.na(ou_type) & !is.na(ga) & ga <= 50 & ga >= 1) %>% 
  mutate(VisitNo=if_else(VisitNo > 6, "7+", as.character(VisitNo))) %>% 
  mutate(VisitNo=as.factor(VisitNo)) %>% 
  mutate(ou_type=recode_factor(ou_type, "FWC"="FWC", "CC"="CC", "Ward"="Ward", "Unit"="Unit" )) %>% 
  group_by(tei) %>% 
  add_tally() %>% 
  arrange(tei, ga) %>% 
  mutate(first = dplyr::first(ou_id)) %>%
  mutate(ga_initial = dplyr::first(ga)) %>% 
  mutate(last_ou = lag(ou_id)) %>% 
  mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou)  ~ "Event 1",
                              first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1", 
                              first != ou_id ~ "Event 2+, different ou as Event 1")) %>% 
  mutate(Moved_ou_wrap = str_wrap(Moved_ou, width = 20))


head(my_data)
## # A tibble: 6 x 14
## # Groups:   tei [4]
##   tei      ga ou_id ou_type name  VisitNo EnrOrgUnit StageUid     n first
##   <chr> <dbl> <chr> <fct>   <chr> <fct>   <chr>      <chr>    <int> <chr>
## 1 A0du~    21 HWOI~ Ward    Ward~ 1       HWOIuGUJV~ Ty22Qt2~     1 HWOI~
## 2 a0lM~    40 P1ok~ CC      Amia~ 1       P1okfUePF~ Ty22Qt2~     2 P1ok~
## 3 a0lM~    40 P1ok~ CC      Amia~ 1       P1okfUePF~ WZbXY0S~     2 P1ok~
## 4 a0Mz~    25 ZiEo~ Unit    Unit~ 1       ZiEozn9m9~ Ty22Qt2~     1 ZiEo~
## 5 a0RW~    18 uMnk~ Ward    Ward~ 1       uMnkQBPF2~ Ty22Qt2~     2 uMnk~
## 6 a0RW~    33 Sr8A~ FWC     Shak~ 1       uMnkQBPF2~ WZbXY0S~     2 uMnk~
## # ... with 4 more variables: ga_initial <dbl>, last_ou <chr>, Moved_ou <chr>,
## #   Moved_ou_wrap <chr>

3 Visualization

Now to the fun stuff! Let’s start simple with a histogram. GA of each visit, by OU type.

p<-ggplot(my_data, aes(ga))+
  geom_histogram(bins=25)+
  facet_wrap(~ou_type, ncol=1)+
  labs(title="Events by GA and OU type")
p

3.1 Density Plot by Org Unit type and Gestational Age

Another way to show this histogram is a density dot plot. To make it interesting we can animate it, to emphasize this progression of time. It looks a bit like a paint roller…

p<-ggplot(my_data, aes(ga, ou_type)) +
  geom_jitter(aes(group = ga, size = .3), height = 0.25, show.legend = FALSE) +
  labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
       subtitle = 'Visits at Gestational Age {closest_state}',
       y = 'Org Unit Type') +
 # scale_colour_manual(values = col_scale) +
  transition_states(ga, transition_length = 3, state_length = 2) +
  shadow_mark(size = .2) +
  ease_aes('linear')

animate(
  plot = p, 
  nframes = 200,
  duration = 15,
  end_pause = 50
)

But this doesnt say much about patient movement. When do clients move to a different org unit clinic?

  • The red dots are the GA and location (org unit) of the first event (identification).

  • The blue dots are subsequent events that are at same location as first event.

  • The green dots are movement to a DIFFERENT location than the identification org unit.

p<-ggplot(my_data, aes(ga, Moved_ou_wrap)) +
  geom_jitter(aes(group = ga, color = Moved_ou_wrap), size = 0.01) +
  facet_wrap(~ou_type, ncol = 1)+
    theme(legend.position = "none") +
    labs(title="Pregnancy events in e-Reg Matlab by Org Unit") +
    ylab("")

p

Same thing, but animated…

p<-p +
  labs(title="Pregnancy events in e-Reg Matlab by Org Unit",
       subtitle = 'Visits at Gestational Age {closest_state}') +
  theme(legend.position = "none") +
  transition_states(ga, transition_length = 3, state_length = 2) +
  shadow_mark(size = .3) +
  ease_aes('linear')

animate(
  plot = p, 
  nframes = 200,
  duration = 15,
  end_pause = 50
)

Now we can see that if a client is not enrolled by an FWA, her first time visiting is often after 36 weeks (Home ANC visit). But if she starts from an HA ward or FWA Unit, she might move to FWC between 14 and 35 weeks. Comparatively fewer women choose to move to CC, if they were identified elsewhere.

This approach tells us what kind of org unit pregnancies GO to, but not what kind of org unit they COME from. We want to narrow in on the patients who start at one org unit, and receive services at another.

3.2 Facet Grid - All Events

We can render the enrollment and event org unit types as two separate dimensions in a grid, and then show a bar chart of event ou category for gestational age ranges.

#What percent of Event 2+ are outside ou of event 1?
my_data %>% 
  ungroup() %>% 
  filter(Moved_ou!="Event 1") %>% 
  group_by(Moved_ou) %>% 
  tally() %>% 
  mutate(percent=n/sum(n))
## # A tibble: 2 x 3
##   Moved_ou                              n percent
##   <chr>                             <int>   <dbl>
## 1 Event 2+, different ou as Event 1   603  0.0778
## 2 Event 2+, same ou as Event 1       7150  0.922
facgrid_data<-my_data %>% 
  ungroup() %>%
    mutate(gestage_event=case_when(
    ga >= 0 & ga < 10 ~ "0-10",
    ga >= 10 & ga < 18 ~ "10-17",
    ga >= 18 & ga < 24 ~ "18-23",
    ga >= 24 & ga < 29 ~ "24-29",
    ga >= 29 & ga < 34 ~ "29-33",
    ga >= 34 & ga < 40 ~ "34-39",
               ga >= 40 ~ "40+",
  )) %>% 
  select("event_ou_type"=ou_type, everything()) %>% 
  left_join(ou_names, by=c("EnrOrgUnit"="ou_id")) %>% 
  rename("enrol"=ou_type) %>% 
  mutate_if(is.factor, as.character) %>% 
  group_by(enrol, event_ou_type, Moved_ou, gestage_event) %>% 
  tally() %>% 
  na.omit()

# facgrid_data %>% 
#   filter(enr_ou_type=="FWC")

facgrid_data %>% 
  ggplot() +
  geom_col(aes(x=gestage_event, y=n, fill=Moved_ou)) +
  facet_grid(enrol ~ event_ou_type, labeller = label_both) +
  labs(title="Total Events by Enrollment OU and Event OU") +
  theme(legend.position="bottom",
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

The grid shows the links between org unit of enrollment and subsequent visits, scaled to show total visits. This efficiently communicates 5 dimensions.

However, the 92% of events after enrollment occur at the same org unit as enrollment. So a major downside of this scale is that we cannot infer trends of crossover between org units (the green dots levels).

3.3 Heat Map

If we focus on the patients who move to a new location (the green dots in dot plot above), we can see the overlap of service provision between types of org unit.

Here is a table of events that are at a different org unit than the enrollment org unit, arranged by org unit type.

pcords_data <-my_data %>% 
  ungroup() %>% 
  filter(Moved_ou=="Event 2+, different ou as Event 1") %>% 
  rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>% 
  left_join(ou_names, by=c("EnrOrgUnit"="ou_id")) %>% 
  rename("enr_ou_type"=ou_type) %>%
  select(enr_ou_type, event_ou_type, "event_ga"=ga, "enr_ga"=ga_initial) 


test<-pcords_data %>%
  arrange(event_ou_type) %>% 
  mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>% 
  group_by(enr_ou_type, event_ou_type) %>% 
  select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type) %>% 
  summarize(count=n())

kable(test) %>%
  kable_styling()
enrollment OU event OU count
CC CC 4
CC FWC 29
CC Unit 9
FWC CC 11
FWC FWC 8
FWC Unit 10
Unit CC 100
Unit FWC 298
Unit Unit 27
Unit Ward 16
Ward CC 14
Ward FWC 73
Ward Unit 4

We can visualize this table graphically in a heatmap

# Give extreme colors:
library(viridis)

ggplot(test, aes(`enrollment OU`, `event OU`, fill= count)) + 
  geom_tile() +
  scale_fill_viridis(discrete=FALSE) +
  theme_minimal()

We can facet these down by enrollment OU, then show the event GA for each subsequent event, again clustering by gestational age.

heat2<-pcords_data %>%
    mutate(event_ou_type=factor(event_ou_type, levels=c("CC","FWC","Unit","Ward"))) %>% 
  mutate(gestage_event=case_when(
    event_ga >= 0 & event_ga < 18 ~ "0-17",
    event_ga >= 18 & event_ga < 24 ~ "18-23",
    event_ga >= 24 & event_ga < 29 ~ "24-29",
    event_ga >= 29 & event_ga < 34 ~ "29-33",
    event_ga >= 34 & event_ga < 40 ~ "34-39",
                     event_ga >= 40 ~ "40+",
  )) %>% 
  group_by(enr_ou_type, event_ou_type, gestage_event) %>% 
  select("enrollment OU"=enr_ou_type, "event OU"=event_ou_type, gestage_event) %>% 
  summarize(count=n())


ggplot(heat2, aes(gestage_event, `event OU`, fill= count)) + 
  geom_tile() +
  scale_fill_viridis(discrete=FALSE) +
  facet_wrap(~`enrollment OU`,labeller = "label_both")+
  labs(title="Events at different OU than enrollment",
       subtitle="By Event Gest Age and OU Type")

This is good, but for interpretability, color is not as perceptible as area. We can take the panel grid from earlier and zero in on the same subset of events as the heat map.

3.4 Facet Grid - CrossOver

facgrid_data %>% 
  filter(Moved_ou =="Event 2+, different ou as Event 1") %>% 
  ggplot() +
  geom_col(aes(x=gestage_event, y=n), fill="darkgreen") +
  facet_grid(enrol ~ event_ou_type, labeller = label_both) +
  labs(title="Events at Different OU than Enrollment") +
  theme(legend.position="bottom",
        axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

This may be the most efficient way to represent org unit crossover in MCH context. The most common follow up visit to a new org unit appears to be a Unit -> FWC transfer, around 18-23 weeks.

3.5 Animated Dot Plot of Transfer Events

If a client moves to a different clinic, at what trimester would she usually make that change? Is it shortly after the first visit, or is there a long delay?

To emphasize the time dimension of transfers, we return to animations. This animation focuses on the “crossover” events, or the green dots in the dot plot above. Thus, each green dot is an event. The horizontal lines represent a type of org unit. The middle grey dots represent the identification events–once they cross that dot, the pregnancy is identified.

After that, clients go to many other types of org units. Some go to a different org unit of same type, while others go to a new org unit type.

By animating this over gestational age at visit, we can see which weeks had high “crossover” of events. The speed of dot movement represents the time between visits.

This animation exemplifies the “Similarity” and “common fate” Gestalt principle: because each dot is the same color and size, the primary information channel is movement from one level to another.

pcord2<-pcords_data %>% 
rownames_to_column(var="id") %>% 
mutate("enr_start"=enr_ou_type) %>% 
pivot_longer(c('enr_start', 'enr_ou_type','event_ou_type'), 
             names_to = "start_finish", 
             values_to="ou_type") %>% 
  mutate(gestage=if_else(start_finish=="enr_ou_type", enr_ga, 
                          if_else(start_finish=="event_ou_type", event_ga, 0))) %>% 
  mutate(endpoint=if_else(start_finish=="enr_start", 0,
                          if_else(start_finish=="enr_ou_type", 1, 2))) %>% 
  mutate(ou_type=factor(ou_type, levels = c("Unit","Ward","FWC","CC"))) %>% 
  mutate(gestage=if_else(endpoint==2 & enr_ga > 30 & gestage > 30, gestage + 2, gestage)) %>% 
  mutate(gestage=if_else(endpoint==2, gestage + 1, gestage))


pcord_summ<-pcord2 %>% 
  filter(endpoint!=0) %>% 
  group_by(endpoint, ou_type, gestage) %>% 
  summarise("count"=n_distinct(id)) %>% 
  mutate("cumsum"=cumsum(count))

#pcord2 %>% filter(endpoint==2 & gestage > 35)


ps1<-pcord_summ %>% filter(endpoint==1)
ps2<-pcord_summ %>% filter(endpoint==2)

p2<-ggplot() +
  geom_point(data = pcord2, aes(x=endpoint, y = ou_type, 
                                group = id), col = "green")  +
  geom_point(data = ps1, aes(x=endpoint, y = ou_type, size = cumsum),
                                col="grey", alpha=0.8) +
  geom_point(data = ps2, aes(x=endpoint, y = ou_type, size = cumsum),
                               col="grey", alpha=0.8) +
  theme_minimal() + 
  transition_reveal(gestage) +
  scale_x_continuous(breaks=c(0, 1, 2),
                   labels=c("GA 0","Identification", "Other Event")) +
  labs(title="e-Reg Matlab -- Events at Different Org Unit than Identification",
       subtitle = 'Events at Gestational Age {round(frame_along)}',
       x = "")

animate(p2,
        nframes = 200,
        duration = 15,
        end_pause = 50)

If a dot moves quickly between identification and subsequent visit, that means that the next visit happened quickly after identification (e.g., Unit identifies pregnancy at 16 weeks, facility visit to CC at 18 weeks). Inversely, the second visit may occur long after identification (if the same client again visits CC at 32 weeks, this would be a slower moving dot).

Eventually we will recreate these data as a Sankey flow diagram across 4 ANC visits. See below.

3.6 Chord Diagram

So far, these visualizations have examined the type of visit, and whether it was different that the location where the pregnancy enrolled. Yet many pregnancies have more than one visit. So we could reframe the question on VISITs to a question about pregnancies: what is the frequency of co-occurrence between different org unit types within pregnancies? What is the per-client overlap?

Chord diagrams might be helpful here. A recent study has shown that chord diagrams can be useful in the context of RMNCH. Data from Kenya’s DHS were rendered as a chord diagram in R to express the year-over-year contraceptive trajectory, or “churn”. When presented to pilot testers at an international conference in Rwanda, testers needed limited support with the interactive visual to understand the “flow” between contraception types.

The below chord diagram can be interpreted as the number of women who visited more than one org unit during their pregnancy, arranged by org unit type. For example, 338 women visited both their FWA Unit and an FWC. Scroll over the links to see all connections.

#STARTER DATA
start<-my_data %>% 
    select(tei, ou_type, name, VisitNo) %>% 
    distinct() %>% 
    select(-VisitNo)
#unique GA weeks/visit

#self join
startend<-start %>% 
  left_join(start, by=c("tei"), suffix=c("_start","_end")) %>% 
  filter(name_start!=name_end) %>% #not a repeat visit/event at same clinic
  ungroup() %>%
  select(-tei, -name_start, -name_end) %>% 
  group_by(ou_type_start, ou_type_end) %>% #group by ou type and tally
  mutate("count"=n()) %>% 
  distinct()

  
#create a co-occurence matrix
dm<-as.matrix(igraph::as_adjacency_matrix(as_tbl_graph(startend),attr = "count"))


#create diagram
library(chorddiag)
chord<-chorddiag(data = dm,
#                      groupColors = c("#000000", "#FFDD89", "#957244", "#F26223"),
                      showGroupnames =TRUE ,
                      showTicks =FALSE,
                      tooltipGroupConnector = "    &#x25B6;    ",
                      chordedgeColor = "#B3B6B7"
                      )
chord

Thinking what types of locations provide services to the same pregnancy is a first step to diagramming the cascade of care.

4 Mapping the Cascade of Care

This section shows the SEQUENCE of visits for each patient. Following the previous animations, we can think of these as a cascade, flowing from one location to the next. Note that these data would considers each identification, ANC visit, or home visit stage at a distinct org units or GA as a separate “visit”.

4.1 Dropouts: Isolating Clients with only one event

About 19% of clients have only one event in the system. Maybe they are systematically different for some reason than other clients or events.

For example, what stage was their only event?

What kind of org unit?

The below tables are only one event.

First is by org unit – most of these are the Pregnancy ID stage.

library(kableExtra)
###Isolate those that only havd one event
my_data_iso<-raw4 %>% 
  left_join(ou_names, by=c("EvtOrgUnit"="ou_id")) %>% 
  select(tei=3, ga=8, "ou_id"=EvtOrgUnit, ou_type, name, VisitNo, EnrOrgUnit, StageUid) %>%
  left_join(ps, by = c("StageUid"="id")) %>% 
  mutate(ga=round(as.numeric(ga))) %>% 
  filter(!is.na(ou_type) & !is.na(ga) & 
           ga <= 50 & ga >= 1 &
          str_detect(psname, paste(c("regnanc", "ANC", "Newborn","PNC","Lab"),collapse = '|')) &
          !str_detect(psname, paste(c("Prev","Risk","Manag"),collapse = '|'))) %>% 
  group_by(tei) %>% 
  add_tally() %>% 
  arrange(tei, ga) %>% 
  mutate(first = dplyr::first(ou_id)) %>%
  mutate(last_ou = lag(ou_id)) %>%
  ungroup() %>% 
  mutate(Moved_ou = case_when(first == ou_id & is.na(last_ou)  ~ "Event 1",
                              first == ou_id & !is.na(last_ou) ~ "Event 2+, same ou as Event 1", 
                              first != ou_id ~ "Event 2+, different ou as Event 1"))

# my_data_iso %>%
#   group_by(n) %>%
#   summarise("events"= n()) %>%
#   mutate(percent = round(events/sum(events), 2))



test2<-my_data_iso %>% 
  group_by(tei) %>% 
  filter(n==1) %>% 
  group_by(psname) %>% 
  summarize("stage_count"=n())

test3<-my_data_iso %>% 
  filter(n==1) %>% 
  group_by(ou_type) %>% 
  summarize("ou_type_count"=n())


#kableExtra::kable(test2)
kable(test2) %>% 
  kable_styling()
psname stage_count
ANC 1st visit_ 2
<U+0997><U+09B0><U+09CD><U+09AD><U+0995><U+09BE><U+09B2><U+09C0><U+09A8> <U+09B8><U+09C7><U+09AC><U+09BE><U+09B0> <U+09A4><U+09A5><U+09CD><U+09AF> (Home - ANC record) 4
<U+0997><U+09B0><U+09CD><U+09AD><U+09BE><U+09AC><U+09B8><U+09CD><U+09A5><U+09BE><U+09B0> <U+09B8><U+09A8><U+09BE><U+0995><U+09CD><U+09A4><U+0995><U+09B0><U+09A3> (Pregnancy identification) 2775
ANC 1st visit 1
ANC visit 6
ANC visit_ 2

Next is by org unit. Most of these are the FWA Units.

kable(test3) %>% 
  kable_styling()
ou_type ou_type_count
CC 72
FWC 46
Unit 1887
Ward 785

4.2 Sankey Diagram

A traditional way to display flow of resources or people is through a Sankey Diagram. These can grow from simple to quite technical and complex.

Below we show the scale of movement between visits at different locations. It begins with “ALL” pregnancies on the far left, and ends with “LTFU” (Lost To Follow Up) on the far right. Organization unit types are matched by color, and visit number correspond to the X axis.

Interactivity, provided by the “networkD3” package, helps to drill down to the segmented flows. Scroll over a “node” (org unit type) to show all patients associated with it. Scroll over a “link” to see total number of clients who moved between these org units at the corresponding visit number.

library(networkD3)

cascade_data <-my_data %>% 
  ungroup() %>% 
  rename("event_ou_id"=ou_id, "event_ou_type"=ou_type) %>% 
  select(tei, event_ou_type, "event_ga"=ga, Moved_ou, StageUid, event_ou_id) %>% 
  left_join(ps, by = c("StageUid"="id")) %>% 
  filter(!is.na(event_ou_type) & !is.na(event_ga) & 
           event_ga <= 50 & event_ga >= 1 &
          str_detect(psname, paste(c("regnanc", "ANC"),collapse = '|')) &
          !str_detect(psname, paste(c("Prev","Risk","Manag", "Out"),collapse = '|'))) %>% 
  group_by(tei) %>% 
  distinct(tei, event_ga, event_ou_id, .keep_all = TRUE) %>% 
#  mutate("VisitNo"=if_else(str_detect(psname, "ident"), 1, 2)) %>% 
  arrange(event_ga) %>% 
  mutate("VisitNo"=row_number()) 


cascade_data<-cascade_data %>% 
  mutate("VisitNo"=if_else(VisitNo==1, 0, as.double(VisitNo))) %>% 
  bind_rows(cascade_data %>%  filter(VisitNo == 1)) %>% 
  mutate("Moved_ou"=if_else(VisitNo <=1, "Event 1", Moved_ou)) %>% 
  mutate(event_ou_type=factor(event_ou_type, levels = c("Unit","Ward","FWC","CC"))) %>% 
  mutate(groupid= group_indices()) %>% 
  arrange(tei, VisitNo) %>% 
  mutate(gestage=if_else(VisitNo==0, 0, event_ga)) %>% 
  filter(VisitNo<8) %>% 
  mutate(gestage=if_else(gestage==lag(gestage) & groupid==lag(groupid) & gestage!=0, lag(gestage)+2, gestage))



sank<-cascade_data %>% 
  ungroup() %>% 
  select(groupid, VisitNo, event_ou_type) %>% 
  group_by(groupid) %>% 
  mutate(ev1_type=if_else(VisitNo==0, "ALL", as.character(event_ou_type)),
         ev2_type=if_else(!is.na(lead(ev1_type)), lead(ev1_type), "LTFU")) %>% 
  mutate(source = paste0(ev1_type, '_', VisitNo)) %>%
  mutate(target = paste0(ev2_type, '_', VisitNo+1)) %>%
  mutate(target=if_else(str_detect(target, "LTFU"), "LTFU", target)) %>% 
  ungroup() %>% 
  select(source, target) 


nodes <- data.frame(name = unique(c(sank$source, sank$target)))

links<-sank %>% 
  group_by_all() %>% 
  tally(name = "value")

links$source <- match(links$source, nodes$name) - 1
links$target <- match(links$target, nodes$name) - 1


nodes$name <- sub('_[0-9]+$', '', nodes$name)


sankeyNetwork(Links = links, Nodes = nodes, Source = 'source',
              Target = 'target', Value = 'value', NodeID = 'name')

4.3 Animated Cascade by Visit Number

While the Sankey diagram does a good job of showing scale of flow, it makes little use of the time between visits. At what gestational age to transfers between units and FWC occur?

We can visualize the visit number (x axis), GA at visit (time), and type of org unit at each visit (y axis). The inspiration is this NYTimes infographic

Each dot represents a single patient as she moves through each level of the MCH system. The patient’s dot “rests” at the location of her last visit recorded.

You can see that very few patients get past the 3rd visit at any level. Most of the migration occurs from the FWA Unit level “up” the system to FWC. And compared to CC level, more clients who made it to FWC by the third visit started from another org unit (red dots).

p3<-ggplot() +
  geom_jitter(data = cascade_data, aes(x=VisitNo, y = event_ou_type, 
                                group = groupid, col = Moved_ou), 
                                size=0.5, width = 0.15, height=0.1)  +
  theme_minimal() + 
  transition_reveal(gestage) +
  scale_x_continuous(breaks=c(0:7),
                   labels=c("GA 0", "Visit1", "Visit2",
                            "Visit3","Visit4","Visit5", "Visit6","Visit7"),
                   minor_breaks = NULL) +
  scale_colour_manual(values = c("grey", "red", "darkblue")) +
  labs(title="e-Reg Matlab -- Events 1-7",
       subtitle = 'Events at Gestational Age {round(frame_along)}',
       x = "",
       y="")+
  theme(legend.position = "bottom")


animate(p3,
        nframes = 200,
        duration = 15,
        end_pause = 50)

#gganimate::anim_save("bd_visit1to7.gif")


#only two TEI went to more than 2 org units!!
# cascade_data %>% 
#   group_by(tei) %>% 
#   mutate(ous=n_distinct(event_ou_id)) %>% 
#   filter(ous>2) %>% 
#   arrange(tei)

# cascade_data %>%
#   group_by(tei) %>%
#   filter(gestage>0) %>% 
#   mutate(ous=n_distinct(event_ou_id),
#          range=max(gestage)-min(gestage),
#          maxv=max(VisitNo)) %>%
#   filter(ous>1, maxv>4, range>10, event_ou_type=="Unit") %>% 
#    arrange(tei)

4.3.1 Cascade – Highlighting an Individual Case

It’s very rare that a patient makes more than one transfer between organization units–only two TEI went to three or more org units. But when such transfers do happen, we can highlight this case to put it in context.

We see that this case moves from FWA Unit to CC for ANC 1, back to Unit, then two more visits at CC in rapid succession.

#let's follow the path of one 
cd2<-cascade_data %>% 
  mutate("size"=if_else(tei=="WLgbmeKuEXI", 1, 0.5))


 cd3<-cd2 %>%
   filter(tei=="WLgbmeKuEXI")


#recreate it with a larger dot
p4<-ggplot() +
  geom_jitter(data = cd2, aes(x=VisitNo, y = event_ou_type,
                                group = groupid, size=size,
                                col = Moved_ou, group=groupid),
                          width = 0.15, height=0.1, alpha=0.5)  +
  geom_segment(data = cd3, aes(x=VisitNo, xend=VisitNo, y=0, yend=event_ou_type,
                             group = groupid),
                        color="darkgreen", size=0.8, alpha=0.6) +
  geom_text(data = cd3, aes(x=VisitNo, y=0.8, label="_Highlighted Case",
                             group = groupid),
                        color="darkgreen", size=5, hjust=0, alpha=0.6) +
  theme_minimal() +
  transition_reveal(gestage) +
  scale_size_continuous(guide=NULL) +
  scale_x_continuous(breaks=c(0:7),
                   labels=c("GA 0", "Visit1", "Visit2",
                            "Visit3","Visit4","Visit5", "Visit6","Visit7"),
                   minor_breaks = NULL) +
  scale_colour_manual(values = c("grey", "red", "darkblue")) +
  labs(title="e-Reg Matlab -- Visits 1-7",
       subtitle = 'Visits at Gestational Age {round(frame_along)}',
       x = "",
       y="")+
  theme(legend.position = "bottom")

#p4
animate(p4,
        nframes = 200,
        duration = 15,
        end_pause = 50)

5 Summary and Discussion

This paper has explored some modalities to display multi-dimensional orgUnit crossover data from DHIS2 Tracker. The eRegistries project in Matlab, Bangladesh offered a prime opportunity to explore visualizations of patient migration behavior, since an advanced biometric solution enabled a shared patient record that was accessible to community service providers and clinical staff. As more health systems work with common patient registers, such analyses of patient migration flows will grow more prevalent, more sophisticated, and more complex. Open source client tracking software such as DHIS2 should promote standard visualization frameworks to help health system managers make sense of these patterns.

Initial

~~~ Insert table showing analysis of chart type by visual channel here

LS0tDQp0aXRsZTogIk1DSCBDbGllbnQgTW92ZW1lbnQgaW4gZS1SZWdpc3RyaWVzIE1hdGxhYjogYSBWaXN1YWwgQW5hbHlzaXMiDQphdXRob3I6ICJCcmlhbiBPJ0Rvbm5lbGwiDQpkYXRlOiAiNS84LzIwMjAiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHRvYzogdHJ1ZQ0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogZmFsc2UNCiAgICAgIHNtb290aF9zY3JvbGw6IGZhbHNlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCBlbmNvZGluZz0iVVRGLTgiKQ0KDQpTeXMuc2V0bG9jYWxlKCJMQ19BTEwiLCJCYW5nbGEiKQ0KI2luc3RhbGwucGFja2FnZXMoImRldnRvb2xzIikNCiNpbnN0YWxsLnBhY2thZ2VzKCJodG1sdG9vbHMiKQ0KbGlicmFyeShodG1sdG9vbHMpDQpsaWJyYXJ5KGRldnRvb2xzKQ0KDQoNCiNpbnN0YWxsX3ZlcnNpb24oIm5ldHdvcmtEMyIsIHZlcnNpb24gPSAiMC4yLjEzIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIsIHVwZ3JhZGU9Im5ldmVyIikNCg0KbGlicmFyeShpZ3JhcGgpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShnZ2FuaW1hdGUpDQpsaWJyYXJ5KGh0dHIpDQpsaWJyYXJ5KGpzb25saXRlKQ0KbGlicmFyeShhc3NlcnR0aGF0KQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeSh2aXJpZGlzKQ0KbGlicmFyeSh0aWR5Z3JhcGgpDQpsaWJyYXJ5KG5ldHdvcmtEMykNCg0KDQpkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIm1hdHRmbG9yL2Nob3JkZGlhZyIpDQpwYXNzd29yZDwtcmVhZF9saW5lcygicGFzc3cudHh0IikNCg0KbGlicmFyeShjaG9yZGRpYWcpDQpsaWJyYXJ5KGh0bWx3aWRnZXRzKQ0KYGBgDQoNCg0KIyBJbnRyb2R1Y3Rpb24gDQoNCkFzIG9mIEF1Z3VzdCAyMDIwLCBvdmVyIDc1IGNvdW50cmllcyBoYWQgW2ltcGxlbWVudGVkIERISVMyIFRyYWNrZXJdKGh0dHBzOi8vd3d3LmRoaXMyLm9yZy9pbi1hY3Rpb24pLCBhbiBvcGVuIHNvdXJjZSB0b29sIGRlc2lnbmVkIHRvIFtjYXB0dXJlIGluZGl2dWRhbCBsZXZlbCBoZWFsdGggZGF0YV0oaHR0cHM6Ly9kb2NzLmRoaXMyLm9yZy8yLjM0L2VuL2ltcGxlbWVudGVyL2h0bWwvZGhpczJfdHJhY2tlcl9pbXBsZW1lbnRhdGlvbl9ndWlkZV9mdWxsLmh0bWwjd2hhdC1jYW4tdHJhY2tlci1iZS11c2VkLWZvcikuIFRob3NlIGRlcGxveWVkIHN1cHBvcnQgYSB3aWRlIHNwZWN0cnVtIG9mIGhlYWx0aCBwcm9ncmFtcy0tYW5kIGxheWVyIG9uIGFkZGl0aW9uYWwgY2xpZW50LWNvbW11bmljYXRpb24gYW5kIGRlY2lzaW9uIHN1cHBvcnQgZmVhdHVyZXMtLWJ1dCB3aXRoaW4gdGhlIHJlYWxtIG9mIGRpZ2l0YWwgaGVhbHRoIGludGVydmVudGlvbnMsIERISVMyIFRyYWNrZXIgb2Z0ZW4gZmFsbHMgdW5kZXIgdGhlIGJyb2FkIFtXSE8gY2xhc3NpZmljYXRpb24gb2YgYSAiQ2xpZW50IEhlYWx0aCBSZWNvcmQiXShodHRwczovL2FwcHMud2hvLmludC9pcmlzL2JpdHN0cmVhbS9oYW5kbGUvMTA2NjUvMjYwNDgwL1dITy1SSFItMTguMDYtZW5nLnBkZj9zZXF1ZW5jZT0xKSB3aGljaCBzdXBwb3J0cyAiTG9uZ2l0dWRpbmFsIHRyYWNraW5nIG9mIGNsaWVudHPigJkgaGVhbHRoIHN0YXR1cyBhbmQgc2VydmljZXMiLg0KDQpBIGNlbnRyYWwgZmVhdHVyZSBvZiAqQ2xpZW50IEhlYWx0aCBSZWNvcmRzKiBpcyBhIHNpbmdsZSBjb2hlc2l2ZSByZWNvcmQgZm9yIGVhY2ggY2xpZW50LCB3aGljaCBjYW4gYmUgc2hhcmVkIGFuZCBhY2Nlc3NlZCBhY3Jvc3MgdmFyaW91cyBsZXZlbHMgb2YgdGhlIGhlYWx0aCBzeXN0ZW0uIFRoZSBwb3J0YWJpbGl0eSBvZiBhIGhlYWx0aCByZWNvcmQgaXMgYSBzaWduaWZpY2FudCwgbGlmZS1zYXZpbmcgaW5ub3ZhdGlvbi4gQXMgc29vbiBhcyB0aGUgcGF0aWVudCBpcyBwcm92aWRlZCBhIHVuaXF1ZSBpZGVudGlmaWVyIGNvZGUsIGEgY2FyZSBwcm92aWRlciBpbiB0aGUgaG9zcGl0YWwgbWlnaHQsIGZvciBleGFtcGxlLCBbdHJhbnNtaXQgbGltaXRlZCBjcml0aWNhbCB0ZXN0IHJlc3VsdHNdKGh0dHBzOi8vd3d3Lmdoc3Bqb3VybmFsLm9yZy9jb250ZW50LzEvMi8xNjApIHRvIGEgY29tbXVuaXR5IGhlYWx0aCB3b3JrZXIncyBjbGllbnQgcmVnaXN0ZXIsIGFuZCB2aWNlIHZlcnNhLiANCg0KTm90IG9ubHkgZG9lcyBhIHNoYXJlZCBoZWFsdGggcmVjb3JkIHN0cmVuZ3RoZW4gY29udGludWl0eSBvZiBjYXJlIGFuZCBlbmhhbmNlZCBkZWNpc2lvbiBzdXBwb3J0IHN5c3RlbXMsIGJ1dCBlbmFibGUgKiphbmFseXNpcyBvZiBjbGllbnQgbW92ZW1lbnQgdGhyb3VnaCB0aGUgaGVhbHRoIHN5c3RlbS4qKiBIZWFsdGggc3lzdGVtIG1hbmFnZXJzIGNhbiBhY2Nlc3MgcmVhbC10aW1lIGluZm9ybWF0aW9uIG9uIHJlZmVycmFscyBvciBjYXJlLXNlZWtpbmcgYmVoYXZpb3I7IGxhZyB0aW1lcyBiZXR3ZWVuIGxhYiByZXN1bHQgbm90aWZpY2F0aW9uIGFuZCBvdXRyZWFjaCBieSBhIGZyb250bGluZSBjYXJlIHdvcmtlcjsgaWRlbnRpZnkgY2xpbmljcyB0aGF0ICJsZWFrIiBwYXRpZW50cyB0byBvdGhlciBjbGluaWNzLCBhbmQgdGhvc2UgdGhhdCAiZ2FpbiIgY2xpZW50cyBmcm9tIG90aGVyIGxvY2F0aW9ucy4NCg0KSG93ZXZlciwgREhJUzIgaGFzIG5vdCB0cmFkaXRpb25hbGx5IHN1cHBvcnRlZCBhZHZhbmNlZCBhbmFseXNpcyBvZiBzdWNoIG11bHRpZGltZW5zaW9uYWwgaW5kaXZpZHVhbC1sZXZlbCBkYXRhLiBNb3N0IHByb2dyYW0gaW5kaWNhdG9ycyBvZiBESElTMiBUcmFja2VyIHByb2dyYW1zIGFyZSBhZ2dyZWdhdGVkIGJ5IHRoZSBsb2NhdGlvbiBvZiBkaXNjcmV0ZSBmYWNpbGl0eSB2aXNpdHMgKCJldmVudHMiKSBvciBwYXRpZW50IHJlZ2lzdHJhdGlvbnMgKCJlbnJvbGxtZW50cyIpLiBFc3NlbnRpYWxseSwgdGhlIG9yZ2FuaXphdGlvbiB1bml0ICgib3JnVW5pdCIgb3IgIk9VIikgY2FuIG9ubHkgYmUgdXNlZCBhcyBhIHNpbmdsZSBkaW1lbnNpb24gd2l0aGluIGEgY2hhcnQgb3IgcGl2b3QgdGFibGUuIEFic2VudCBhbnkgZGVzaWduYXRpb25zIG9mIGEgInRyYW5zZmVyIGZyb20iIG9yZyB1bml0IGFuZCBhICJ0cmFuc2ZlciB0byIgb3JnIHVuaXQsIHRoZXJlIGlzIGxpbWl0ZWQgYW5hbHlzaXMgb2YgcGF0aWVudCAiY3Jvc3Mtb3ZlciIgYmV0d2VlbiBmYWNpbGl0aWVzLiBZZXQsIHRoZSByYXcgZGF0YSBpcyBhdmFpbGFibGUgd2l0aGluIERISVMyIHRvIHBlcmZvcm0gYSAiY3Jvc3Mtb3ZlciIgYW5hbHlzaXMgaW4gZXh0ZXJuYWwgc29mdHdhcmUsIHN1Y2ggYXMgUi4gDQoNClRoZSBnb2FsIG9mIHRoaXMgcGFwZXIgaXMgdG8gcHJlc2VudCBleHBsb3JhdG9yeSB2aXN1YWxpemF0aW9ucyB0aGF0IGNvbnZleSBjbGllbnQgbW92ZW1lbnQgcGF0dGVybnMgZm91bmQgaW4gREhJUzIsIGFuZCBzdXBwb3J0IGRldmVsb3BtZW50IG9mIGEgZ2VuZXJpYyBhbmFseXRpYyBmcmFtZXdvcmsgdG8gdmlzdWFsaXplIHRoZXNlIGRhdGEgYXMgYSBuYXRpdmUgREhJUzIgZmVhdHVyZS4gVGhpcyB3b3JrIHNob3VsZCBiZSBzZWVuIGFzICpleHBlcmltZW50YWwqIGFuZCBub3QgcHJlc2NyaXB0aXZlLiBDb2RlIGlzIHByb3ZpZGVkIHRvIGVuY291cmFnZSByZWFkZXIgZXhwZXJpbWVudGF0aW9uIGFuZCBjb21tZW50LiBGdXJ0aGVybW9yZSwgc2VwYXJhdGUgcmVzZWFyY2ggd2lsbCBiZSBjb25kdWN0ZWQgdG8gdW50YW5nbGUgdGhlIGRldGVybWluYW50cyBmb3IgcGF0aWVudCBtb3ZlbWVudCBzcGVjaWZpYyB0byB0aGUgY2FzZSBzdHVkeSBpbiBCYW5nbGFkZXNoLg0KDQoNCiMjIFZpc3VhbCBFbmNvZGluZyBhbmQgR2VzdGFsdCBQcmluY2lwbGVzDQoNClRvIGFzc2VzcyB0aGUgYXBwcm9wcmlhdGVuZXNzIGFuZCBlZmZlY3RpdmVuZXNzIG9mIGVhY2ggZGF0YSB2aXN1YWxpemF0aW9uIGZvcm1hdCwgdGhpcyBwYXBlciB3aWxsIGRlc2NyaWJlIHRoZSB0ZXN0ZWQgdmlzdWFsaXphdGlvbnMgdGhyb3VnaCBHZXN0YWx0IHByaW5jaXBsZXMgYW5kIFdpbGxpYW0gQ2xldmVsYW5kJ3MgY2hhbm5lbHMgZm9yIG1hcHBpbmcgZGF0YS4NCg0KDQoqKlZpc3VhbCBDaGFubmVsIFJhbmtpbmdzKioNCg0KQSBudW1iZXIgb2YgcHN5Y2hvbG9naWNhbCBleHBlcmltZW50cyBoYXZlIGJlZW4gY29uZHVjdGVkIHRvIHJldmVhbCB3aGljaCBpbmZvcm1hdGlvbiBjaGFubmVscyBhcmUgbW9zdCBleHByZXNzaXZlIGFuZCBhY2N1cmF0ZSB3aGVuIHZpc3VhbGx5IHBlcmNlaXZlZC4gRm9yIGV4YW1wbGUsIFdpbGxpYW0gUy4gQ2xldmVsYW5kIGFuZCBSb2JlcnQgTWNHaWxsIGFza2VkIHBhcnRpY2lwYW50cyB0byBlc3RpbWF0ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHR3byB2YWx1ZXMgd2l0aGluIGEgY2hhcnQgZS5nLiwgdHdvIGJhcnMgaW4gYSBiYXIgY2hhcnQsIG9yIHR3byBzbGljZXMgb2YgYSBwaWUgY2hhcnQuIEZyb20gdGhlIGVycm9ycyByZWNvcmRlZCwgdGhleSBkZXRlcm1pbmUgdGhhdCBudW1lcmljIGluZm9ybWF0aW9uIGlzIG1vcmUgZWZmZWN0aXZlbHkgY29tbXVuaWNhdGVkIHRocm91Z2ggdGhlIGxlbmd0aCAoYmFyIGNoYXJ0KSB0aGFuIGFuZ2xlIChwaWUgY2hhcnQpLiBUaGVpciByZXN1bHRzIGhhdmUgYmVlbiBjb2xsYXRlZCBpbnRvIGEgaGllcmFyY2h5IG9mIHZpc3VhbCBjaGFubmVscyB0byBleHByZXNzIGJvdGggb3JkZXJlZCBhbmQgY2F0ZWdvcmljYWwgYXR0cmlidXRlcy4gVGhlIHJhbmtpbmdzIGFyZSBvdXRsaW5lZCBpbiBUYW1hcmEgTXVuemVyJ3MgdGV4dGJvb2sgWyJWaXN1YWwgQW5hbHlzaXMgJiBEZXNpZ24iXShodHRwczovL3d3dy5jcy51YmMuY2EvfnRtbS92YWRib29rLyksIGJlbG93LCBhbmQgeW91IGNhbiByZWFkIG1vcmUgYWJvdXQgQ2xldmVsYW5kJ3Mgb3JpZ2luYWwgW2V4cGVyaW1lbnRzIGhlcmUuXShodHRwczovL3NvY3Zpei5jby9sb29rYXRkYXRhLmh0bWwpIA0KDQpGdW5kYW1lbnRhbGx5LCB0aGlzIHJhbmtpbmcgb2YgdmlzdWFsIGNoYW5uZWxzIHdpbGwgaGVscCBhIGRlc2lnbmVyIG1hdGNoIHRoZSBzYWxpZW5jZSBvZiBhIGdpdmVuIGRhdGEgZGltZW5zaW9uIHRvIGl0cyBwcm9taW5lbmNlIHdpdGhpbiB0aGUgdmlzdWFsaXphdGlvbi4NCg0KDQpgYGB7ciBtdW56ZXIsIGVjaG89RkFMU0UsIGZpZy5jYXA9Ik11bnplciAyMDE0LCBmaWcgNS42LCBQZyAxMDIiLCBvdXQud2lkdGggPSAnNjAlJ30NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJtdW56ZXIyMDE0LmpwZyIpDQpgYGANCg0KDQoNCioqR2VzdGFsdCBQcmluY2lwbGVzKioNCg0KQnV0IGluIHZpc3VhbGl6YXRpb24sIGFzIHRoZSBzYXlpbmcgZ29lcywg4oCcdGhlIHdob2xlIGlzIG1vcmUgdGhhbiB0aGUgc3VtIG9mIHRoZSBwYXJ0c+KAnS4gVGhlIEdlc3RhbHQgUHJpbmNpcGxlcywgZGV2ZWxvcGVkIGJ5IEdlcm1hbiBwc3ljaG9sb2dpc3RzIGluIHRoZSAxOTIwcywgaGVscCBleHBsYWluIHRoZSBfaG9saXN0aWNfIFtjaGFyYWN0ZXJpc3RpY3Mgb2YgcGVyY2VwdGlvbl0oaHR0cDovL3d3dy5zY2hvbGFycGVkaWEub3JnL2FydGljbGUvR2VzdGFsdF9wcmluY2lwbGVzKSBpbiBkYXRhIHZpc3VhbGl6YXRpb24uDQoNClRha2VuIHRvZ2V0aGVyLCBHZXN0YWx0IFByaW5jaXBsZXMgc3VnZ2VzdCBob3cgdXNlcnMgd2lsbCByZWFkIGFuZCBpbnRlcnByZXQgZXhwZXJpbWVudGFsIHZpc3VhbGl6YXRpb25zIGFzIGEgd2hvbGUsIHByb3ZpZGluZyBhIHVzZWZ1bCBkZXNpZ24gZnJhbWV3b3JrIGRyYXdpbmcgZHJhdyB1c2VycycgYXR0ZW50aW9uIHRvIGtleSBwYXJ0cyBvZiBhIGdyYXBoLiBWaXN1YWwgY2hhbm5lbHMgc3VtbWFyaXplIGhvdyBlYWNoIF9wYXJ0XyBvZiBhIHZpc3VhbGl6YXRpb24gaXMgcGVyY2VpdmVkOyBHZXN0YWx0IFByaW5jaXBsZXMgYXNzZXNzIHRoZSBfd2hvbGVfLg0KDQpUaGUgc2V2ZW4gcHJpbmNpcGxlcyBhcmUuLi4NCiogUHJveGltaXR5OiBUaGluZ3MgdGhhdCBhcmUgc3BhdGlhbGx5IG5lYXIgdG8gb25lIGFub3RoZXIgc2VlbSB0byBiZSByZWxhdGVkLg0KKiBTaW1pbGFyaXR5OiBUaGluZ3MgdGhhdCBsb29rIGFsaWtlIHNlZW0gdG8gYmUgcmVsYXRlZC4NCiogQ29ubmVjdGlvbjogVGhpbmdzIHRoYXQgYXJlIHZpc3VhbGx5IHRpZWQgdG8gb25lIGFub3RoZXIgc2VlbSB0byBiZSByZWxhdGVkLg0KKiBDb250aW51aXR5OiBQYXJ0aWFsbHkgaGlkZGVuIG9iamVjdHMgYXJlIGNvbXBsZXRlZCBpbnRvIGZhbWlsaWFyIHNoYXBlcy4NCiogQ2xvc3VyZTogSW5jb21wbGV0ZSBzaGFwZXMgYXJlIHBlcmNlaXZlZCBhcyBjb21wbGV0ZS4NCiogRmlndXJlIGFuZCBHcm91bmQ6IFZpc3VhbCBlbGVtZW50cyBhcmUgdGFrZW4gdG8gYmUgZWl0aGVyIGluIHRoZSBmb3JlZ3JvdW5kIG9yIHRoZSBiYWNrZ3JvdW5kLg0KKiBDb21tb24gRmF0ZTogRWxlbWVudHMgc2hhcmluZyBhIGRpcmVjdGlvbiBvZiBtb3ZlbWVudCBhcmUgcGVyY2VpdmVkIGFzIGEgdW5pdC4NCg0KDQpgYGB7ciwgb3V0LndpZHRoPSc4MCUnLCBmaWcuY2FwdGlvbj0iR2VzdGFsdCBQcmluY2lwbGUgb2YgQ29ubmVjdGlvbiJ9DQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwOi8vd3d3LnNjaG9sYXJwZWRpYS5vcmcvdy9pbWFnZXMvdGh1bWIvMS8xOC9Ub2Rvcm92aWMtR2VzdGFsdF9wcmluY2lwbGVzLUZpZ3VyZV80LmpwZy84MDBweC1Ub2Rvcm92aWMtR2VzdGFsdF9wcmluY2lwbGVzLUZpZ3VyZV80LmpwZyIpDQoNCg0KYGBgDQoNCg0KUHJldmlvdXMgW3Jlc2VhcmNoXShodHRwczovL2llZWV4cGxvcmUuaWVlZS5vcmcvZG9jdW1lbnQvMTAyODg1OSkgc3VnZ2VzdHMgdGhlc2UgcHJpbmNpcGxlcyBhcmUgZXNwZWNpYWxseSBzYWxpZW50IHdoZW4gY29uc2lkZXJpbmcgaW50ZXJhY3RpdmUgb3IgW2FuaW1hdGVkIHZpc3VhbHNdKGh0dHA6Ly91c2Vycy51bWlhY3MudW1kLmVkdS9+ZWxtL3Byb2plY3RzL2NvbW1vbi1mYXRlL2NvbW1vbi1mYXRlLnBkZiksIHdoaWNoIGFyZSBsZXNzIGNvbW1vbiBpbiB0aGUgY29udGV4dCBvZiBESElTMiBvciBSTU5DSC4NCg0KDQojIyBUaGUgTWF0bGFiIGVSZWdpc3RyeSBjb250ZXh0DQoNCkRhdGEgd2VyZSBjb2xsZWN0ZWQgYXMgcGFydCBvZiBhbiBvbmdvaW5nIFtyYW5kb21pemVkIGNvbnRyb2xsZWQgdHJpYWwgaW4gTWF0bGFiLCBCYW5nbGFkZXNoXShodHRwOi8vd3d3LmlzcmN0bi5jb20vSVNSQ1RONjk0OTE4MzYpLiBCcm9hZGx5LCB0aGUgcHJpbWFyeSByZXNlYXJjaCBxdWVzdGlvbiBpcyB3aGV0aGVyIGEgY29tcHJlaGVuc2l2ZSBlUmVnaXN0cmllcyBzeXN0ZW0gd2l0aCBkZWNpc2lvbiBzdXBwb3J0IGFuZCB0YXJnZXRlZCBjbGllbnQgY29tbXVuaWNhdGlvbiwgY29tcGFyZWQgdG8gYSBjb250cm9sIGVsZWN0cm9uaWMgZGF0YSBlbnRyeSBzY3JlZW4gd2l0aG91dCBmZWVkYmFjaywgbGVhZHMgdG8gaW1wcm92ZWQgcXVhbGl0eSBvZiBjYXJlIGluIG1hdGVybmFsIGhlYWx0aCBzeXN0ZW0uDQoNClRoZSBzeXN0ZW0gd2FzIGJ1aWx0IG9uIHRvcCBvZiBESElTMiBUcmFja2VyLCBhbmQgcnVuIG9uIGJvdGggQW5kcm9pZCBkZXZpY2VzIGFuZCBjaHJvbWVib29rcy4gSXQgcHJlc2VudHMgYW4gaWRlYWwgdXNlIGNhc2UgdG8gZXhhbWluZSBPcmcgVW5pdCBjcm9zcy1vdmVyLCBmb3IgdHdvIGtleSByZWFzb25zLiANCg0KRmlyc3QsIHRoZSBzeXN0ZW0gZW1wbG95cyBhIG5vdmVsIGFwcHJvYWNoIHRvIGEgbWFpbiBjaGFsbGVuZ2Ugb2YgY2xpZW50IGhlYWx0aCByZWNvcmRzIGluIGluIGxvdy1yZXNvdXJjZSBzZXR0aW5nczogdW5pcXVlIGNsaWVudCBpZGVudGlmaWNhdGlvbi4gRm9yIHRoZSBwdXJwb3NlcyBvZiB0aGUgdHJpYWwsIDk5LjklIG9mIGNsaWVudHMgY29uc2VudGVkIHRvIGlkZW50aWZpY2F0aW9uIHdpdGggYSBwYWxtLWJhc2VkIGJpb21ldHJpYyBzeXN0ZW0gZGV2ZWxvcGVkIGJ5IEVsZW1lbnQsIEluYy4sIHdoaWNoIGxldmVyYWdlcyB0aGUgYnVpbHQtaW4gY2FtZXJhIG9uIG1vYmlsZSBkZXZpY2VzIHRvIGltYWdlIHBhbG1wcmludHMgYW5kIGdlbmVyYXRlIHVuaXF1ZSBpZGVudGlmaWVycyB0aGF0IGFyZSBjb3BpZWQgaW50byB0aGUgREhJUzIgYXBwLiBUaGUgYXBwbGljYXRpb24gcnVucyBvbiB0aGUgc2FtZSBtb2JpbGUgZGV2aWNlcyB1c2VkIHRvIGVudGVyIGRhdGEsIGFuZCBhbHNvIGhhcyB0aGUgYWJpbGl0eSB0byB3b3JrIG9mZmxpbmUgaW4gYXJlYXMgd2l0aCBwb29yIGNvbm5lY3Rpdml0eS4gSW4gc2VwYXJhdGUgdHJpYWxzIG9uIGEgc3Vic2V0IG9mIDE1MCBjbGllbnRzIGluIE1hdGxhYiwgODQlIG9mIGNhcmUgcHJvdmlkZXJzIHdlcmUgYWJsZSB0byBjb3JyZWN0bHkgaWRlbnRpZnkgYSBwYXRpZW50IHdpdGggdGhlIGFwcCBbb24gdGhlIGZpcnN0IHNjYW5dKGh0dHA6Ly9nbG9iYWxoZWFsdGgubm8vYXNzZXRzL2ltZy9BYnN0cmFjdC1ib2sucGRmKTsgMTAwJSB3ZXJlIGlkZW50aWZpZWQgd2l0aGluIDMgYXR0ZW1wdHMuDQoNCldoaWxlIGR1cGxpY2F0ZSByZWNvcmRzIG1heSBzdGlsbCBiZSBwb3NzaWJsZSB3aXRoaW4gdGhlIGRhdGFiYXNlLCBkdXBsaWNhdGVzIGFyZSBtdWNoIGxlc3MgY29tbW9uIHRoYW4gb3RoZXIgY29tbXVuaXR5LWxldmVsIGNsaWVudCBoZWFsdGggcmVjb3JkcywgZHVlIHRvIGFuIHVsdHJhLXBvcnRhYmxlLCBoaWdobHkgYWNjdXJhdGUgYmlvbWV0cmljIElEIHN5c3RlbS4NCg0KVGhlIHR3ZWV0IGJlbG93IGlsbHVzdHJhdGVzIHRoaXMgd29ya2Zsb3cgaW4gcHJhY3RpY2UuDQoNCmBgYHtyIHR3ZWV0LCBvdXQud2lkdGg9JzUwJSd9DQoNCnR3ZWV0cm1kOjp0d2VldF9lbWJlZCgiaHR0cHM6Ly90d2l0dGVyLmNvbS9lUmVnaXN0cmllcy9zdGF0dXMvMTA2NDU4NTMzNDIxNjc0OTA1NyIpDQoNCiNrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9wYnMudHdpbWcuY29tL21lZGlhL0RzWXBIYU1WWUFBakNzWT9mb3JtYXQ9anBnJm5hbWU9c21hbGwiKQ0KYGBgDQoNCg0KDQpTZWNvbmQsIHRoZSBzeXN0ZW0gZW5jb3VyYWdlcyBzaGFyaW5nIG9mIHJlY29yZHMgYWNyb3NzIG9yZyB1bml0cy4gSW5kZWVkLCB0aGUgKmxpbmsgYmV0d2VlbiBjb21tdW5pdHktbGV2ZWwgaWRlbnRpZmljYXRpb24gYW5kIGZhY2lsaXR5LWxldmVsIHNlcnZpY2UgZGVsaXZlcnkgaXMgYnVpbHQgaW50byB0aGUgc3RhbmRhcmQgd29ya2Zsb3cqLiBBcyBkZXNpZ25lZCwgYSBjb21tdW5pdHkgaGVhbHRoIHdvcmtlciBjYW4gaWRlbnRpZnkgYSBwcmVnbmFuY3kgaW4gZmlyc3QgdHJpbWVzdGVyLCB0aGVuIGVuY291cmFnZSB0aGUgY2xpZW50IHRvIHNlZWsgY2xpbmljYWwgY2FyZS4gV2hlbiB0aGUgIGxhdGVyIG9wZW5zIHRoZSBjbGllbnQgcmVjb3JkLCBzaGUgYWxyZWFkeSBoYXMgdGhlIGdlc3RhdGlvbmFsIGFnZSBjYWxjdWxhdGVkIGJ5IHRoZSBjb21tdW5pdHkgaGVhbHRoIHdvcmtlci4gU28sIGlmIGEgaGlnaCBibG9vZCBwcmVzc3VyZSByZWFkaW5nIGlzIGVudGVyZWQsIHRoZSBkaWFnbm9zaXMgd291bGQgYmUgYXV0b21hdGljYWxseSB3cml0dGVuIHRvIHRoZSByZWNvcmQgYXMgZWl0aGVyIGNocm9uaWMgb3IgZ2VzdGF0aW9uYWwgaHlwZXJ0ZW5zaW9uLCBkZXBlbmRlbnQgb24gdHJpbWVzdGVyIG9mIHZpc2l0LiBUaGUgZGlhZ25vc2VzIHdvdWxkIHRoZW4gYmUgdmlzaWJsZSB0byB0aGUgY29tbXVuaXR5IHdvcmtlciBhdCB0aGUgbmV4dCB2aXNpdC4NCg0KDQoNCiMjIyBOb3RlIG9uIE9yZyBVbml0IEhpZXJhcmNoeSBvZiBNQ0ggU2VydmljZXMgaW4gQmFuZ2xhZGVzaA0KDQpGb3Igb3VyIHB1cnBvc2VzIGl0IGlzIGltcG9ydGFudCB0byBrbm93IHdoaWNoIHdvcmtlcnMgYW5kIG9yZ2FuaXN6YXRpb24gdW5pdHMgKGFrYSAib3JnIHVuaXRzIiBvciAiT1VzIikgcHJvdmlkZSByZXByb2R1Y3RpdmUgaGVhbHRoIHNlcnZpY2VzIGluIE1hdGxhYi4gVGhlcmUgYXJlIHR3byBkaXJlY3RvcmF0ZXMgdW5kZXIgdGhlIEJhbmdsYWRlc2ggTWluaXN0cnkgb2YgSGVhbHRoIGFuZCBGYW1pbHkgV2VsZmFyZSB0aGF0IHByb3ZpZGUgc2VydmljZXMsIHdpdGggY2FkcmVzIG9mIGNvbW11bml0eSBhbmQgZmFjaWxpdHkgYmFzZWQgaGVhbHRoIHdvcmtlcnMuIEluIHBhcnRpY3VsYXIsICoqRmFtaWx5IFdlbGZhcmUgQXNzaXN0YW50cyoqIGFyZSByZXNwb25zaWJsZSBmb3IgRmFtaWx5IFBsYW5uaW5nIHNlcnZpY2VzIHRvIHRoZWlyIEZXQSAqKlVuaXRzKiouIFRoZXkgcm91dGluZWx5IHZpc2l0IGFsbCBob3VzZWhvbGRzIGluIHRoZSBhcmVhIHdpdGggd29tZW4gb2YgcmVwcm9kdWN0aXZlIGFnZSB0byBjb3Vuc2VsLCBzaGFyZSBjb250cmFjZXB0aW9uIG9wdGlvbnMsIGFuZCBvZmZlciBwcmVnbmFuY3kgdGVzdHMuICoqSGVhbHRoIEFzc2lzdGFudHMqKiBoYXZlIGNvbW11bml0eSBvdXRwb3N0cyB0byBwcm92aWRlIHZhY2NpbmF0aW9uIHNlcnZpY2VzIGF0ICoqV2FyZCoqIGxldmVsLiBBbnkgb2YgdGhlc2UgaGVhbHRoIHdvcmtlcnMsIHBsdXMgdGhlIGZhY2lsaXR5IGJhc2VkIHN0YWZmIGF0IENvbW11bml0eSBDbGluaWNzIGFuZCBGYW1pbHkgV2VsZmFyZSBDZW50cmVzLCBjYW4gaWRlbnRpZnkgYSBwcmVnbmFuY3kgd2l0aGluIHRoZSBlLVJlZ2lzdHJ5IHN5c3RlbSwgb24gY2hyb21lYm9va3Mgb3IgdGFibGV0cyBwcm92aWRlZCBieSB0aGUgcHJvamVjdC4NCg0KDQpgYGB7cn0NCg0Kb3VfdGFibGU8LXRpYmJsZSgiRGlyZWN0b3JhdGUgR2VuZXJhbCBvZiBGYW1pbHkgUGxhbm5pbmciPWMoIkZhbWlseSBXZWxmYXJlIENlbnRlciAoRldDKSIsIkZhbWlseSBXZWxmYXJlIEFzc2lzdGFudCAoRldBKSBVbml0IiksDQogICAgICAgICAgICAgICAgICJEaXJlY3RvcmF0ZSBHZW5lcmFsIG9mIEhlYWx0aCBTZXJ2aWNlcyI9YygiQ29tbXVuaXR5IENlbnRyZSAoQ0MpIiwiSGVhbHRoIEFzc2lzdGFudCAoSEEpIFdhcmQiKSkNCg0Kcm93bmFtZXMob3VfdGFibGUpPC1jKCJGYWNpbGl0eSIsIkNvbW11bml0eSIpDQoNCmthYmxlKG91X3RhYmxlKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KICANCg0KYGBgDQoNCg0KIyBEYXRhIEltcG9ydCBhbmQgUHJvY2Vzc2luZw0KDQpUaGVzZSBhbmFseXNlcyBwdWxsIGRhdGEgZnJvbSBwcm9kdWN0aW9uIGFuZCBkZXZlbG9wbWVudCBESElTMiBlbnZpcm9ubWVudHMgdG8gYXNzZXNzIGNsaWVudCBtb3ZlbWVudC4NCg0KUmF3IGRhdGEgd2VyZSBwdWxsZWQgZnJvbSBwcm9kdWN0aW9uIHRocm91Z2ggU1FMIHZpZXcgYnkgdGhlIGltcGxlbWVudGluZyBwYXJ0bmVyLCBhbmQgbG9vayBsaWtlIHRoaXM6DQoNCmBgYHtyIHByb2QgZGF0YX0NCg0KI3JlYWQgZmlsZSBmcm9tIHByb2R1Y3Rpb24gYW5kIHZpZXcNCg0KcmF3NDwtc3VwcHJlc3NNZXNzYWdlcyhyZWFkX2NzdigiQnJpYW5Ecy5jc3YiKSkNCg0KI1ZpZXcocmF3NCkNCiNjb2xuYW1lcyhyYXc0KQ0KDQpoZWFkKHJhdzQpDQoNCmBgYA0KDQoNClRvIHByb3ZpZGUgY29udGV4dCwgbWV0YWRhdGEgZnJvbSBkZXZlbG9wbWVudCBlbnZpcm9ubWVudA0KDQpgYGB7ciBkZXYgZGF0YX0NCiNub3cgZ2V0IGJhY2tncm91bmQgZGF0YSBmcm9tIGRldg0KI2dldCBPcmcgVW5pdCBncm91cHMNCmJhc2V1cmw8LSJodHRwczovL2JkLWVyZWdpc3RyeS5kaGlzMi5vcmcvZGhpcy8iDQp1c2VybmFtZTwtImluZ190ZXN0Ig0KDQoNCiNmdW5jdGlvbiBmb3IgbG9nZ2luZyBpbg0KbG9naW5ESElTMjwtZnVuY3Rpb24oYmFzZXVybCx1c2VybmFtZSxwYXNzd29yZCkgew0KICB1cmw8LXBhc3RlMChiYXNldXJsLCJhcGkvbWUiKQ0KICByPC1HRVQodXJsLGF1dGhlbnRpY2F0ZSh1c2VybmFtZSxwYXNzd29yZCkpDQogIHdhcm5fZm9yX3N0YXR1cyhyLCB0YXNrPSJsb2cgaW4iKQ0KICBpZihyJHN0YXR1c19jb2RlID09IDIwMEwpe3JldHVybihUUlVFKX0NCn0NCg0KDQppZihsb2dpbkRISVMyKGJhc2V1cmwsIHVzZXJuYW1lLCBwYXNzd29yZCk9PVRSVUUpew0KICBwcmludCgic3VjY2Vzc2Z1bGx5IGxvZ2dlZCBpbiIpDQp9ZWxzZXsNCiAgc3RvcCgiY291bGQgbm90IGxvZyBpbiEgUGxlYXNlIGNoZWNrIHVybCwgdXNlcm5hbWUgYW5kIHBhc3N3b3JkIikNCn0NCg0KI2dyb3Vwcw0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS9vcmdhbmlzYXRpb25Vbml0R3JvdXBzLmpzb24/cGFnaW5nPWZhbHNlJmZpZWxkcz1pZCxuYW1lLG9yZ2FuaXNhdGlvblVuaXRzIikNCm91X2dyb3VwczwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwgInRleHQiKSwgZmxhdHRlbiA9IFRSVUUpICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgc2VsZWN0KCJuYW1lIj0xLCJpZCI9MiwibWVtYmVycyI9MykgJT4lIA0KICB1bm5lc3RfbG9uZ2VyKG1lbWJlcnMpICU+JSANCiAgZmxhdHRlbigpDQpoZWFkKG91X2dyb3VwcykNCiAgDQojc3RhZ2VzDQp1cmw8LXBhc3RlMChiYXNldXJsLCAiYXBpL3Byb2dyYW1TdGFnZXMuanNvbj9wYWdpbmc9ZmFsc2UmZmllbGRzPWlkLG5hbWUiKQ0KcHM8LWZyb21KU09OKGNvbnRlbnQoR0VUKHVybCksICJ0ZXh0IiksIGZsYXR0ZW4gPSBUUlVFKSAlPiUgDQogIGRhdGEuZnJhbWUoKSAlPiUgDQogIHNlbGVjdCgiaWQiPTIsICJwc25hbWUiPTEpDQoNCnRhaWwocHMpDQoNCiNPVSBuYW1lcw0KdXJsPC1wYXN0ZTAoYmFzZXVybCwgImFwaS9vcmdhbmlzYXRpb25Vbml0cy5qc29uP3BhZ2luZz1mYWxzZSZmaWVsZHM9aWQsbmFtZSIpDQpvdV9uYW1lczwtZnJvbUpTT04oY29udGVudChHRVQodXJsKSwgInRleHQiKSwgZmxhdHRlbiA9IFRSVUUpICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgc2VsZWN0KCJuYW1lIj0xLCAib3VfaWQiPTIpICU+JSANCiAgbXV0YXRlKCJvdV90eXBlIj1jYXNlX3doZW4oDQogICAgc3RyX2RldGVjdChuYW1lLCAiIEZXQyIpIH4gIkZXQyIsDQogICAgc3RyX2RldGVjdChuYW1lLCAiKD9pKUNDIikgfiAiQ0MiLA0KICAgIHN0cl9kZXRlY3QobmFtZSwgIlVuaXQiKSB+ICJVbml0IiwNCiAgICBzdHJfZGV0ZWN0KG5hbWUsICJXYXJkIikgfiAiV2FyZCIpKQ0KDQpoZWFkKG91X25hbWVzKQ0KDQoNCg0KYGBgDQoNCiMjIFByb2Nlc3NpbmcNCg0KV2UgdGhlbiBtZXJnZSB0aGUgcmF3IG91dHB1dHMgb2YgVEVJIGV2ZW50cyB3aXRoIG9yZyB1bml0IGluZm9ybWF0aW9uLg0KDQpXaGVuIGFycmFuZ2luZyBldmVudHMgYnkgdmlzaXQgbnVtYmVyLCB3ZSBjbGFzc2lmeSBzdWJzZXF1ZW50IHZpc2l0cyBhcyBiZWluZyBhdCB0aGUgc2FtZSBvciBkaWZmZXJlbnQgb3JnIHVuaXQgdGhhbiB0aGUgaW5pdGlhbCB2aXNpdC4NCg0KYGBge3IgcHJvY2Vzc2luZ30NCg0KbXlfZGF0YTwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUgDQogIGRpc3RpbmN0KCkgJT4lICNldmVudCBtdXN0IGJlIHVuaXF1ZSB2aXNpdCwgaS5lLiBhbiBBTkMgbWFuYWdlbWVudCBhbmQgQU5DIHN0YWdlIHNhbWUgd2VlayB3b3VsZCBiZSBtZXJnZWQNCiAgbXV0YXRlKGdhPXJvdW5kKGFzLm51bWVyaWMoZ2EpKSkgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKG91X3R5cGUpICYgIWlzLm5hKGdhKSAmIGdhIDw9IDUwICYgZ2EgPj0gMSkgJT4lIA0KICBtdXRhdGUoVmlzaXRObz1pZl9lbHNlKFZpc2l0Tm8gPiA2LCAiNysiLCBhcy5jaGFyYWN0ZXIoVmlzaXRObykpKSAlPiUgDQogIG11dGF0ZShWaXNpdE5vPWFzLmZhY3RvcihWaXNpdE5vKSkgJT4lIA0KICBtdXRhdGUob3VfdHlwZT1yZWNvZGVfZmFjdG9yKG91X3R5cGUsICJGV0MiPSJGV0MiLCAiQ0MiPSJDQyIsICJXYXJkIj0iV2FyZCIsICJVbml0Ij0iVW5pdCIgKSkgJT4lIA0KICBncm91cF9ieSh0ZWkpICU+JSANCiAgYWRkX3RhbGx5KCkgJT4lIA0KICBhcnJhbmdlKHRlaSwgZ2EpICU+JSANCiAgbXV0YXRlKGZpcnN0ID0gZHBseXI6OmZpcnN0KG91X2lkKSkgJT4lDQogIG11dGF0ZShnYV9pbml0aWFsID0gZHBseXI6OmZpcnN0KGdhKSkgJT4lIA0KICBtdXRhdGUobGFzdF9vdSA9IGxhZyhvdV9pZCkpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpICU+JSANCiAgbXV0YXRlKE1vdmVkX291X3dyYXAgPSBzdHJfd3JhcChNb3ZlZF9vdSwgd2lkdGggPSAyMCkpDQoNCg0KaGVhZChteV9kYXRhKQ0KDQpgYGANCg0KDQoNCiMgVmlzdWFsaXphdGlvbiANCg0KTm93IHRvIHRoZSBmdW4gc3R1ZmYhIExldCdzIHN0YXJ0IHNpbXBsZSB3aXRoIGEgaGlzdG9ncmFtLiBHQSBvZiBlYWNoIHZpc2l0LCBieSBPVSB0eXBlLg0KDQpgYGB7ciBoaXN0IGJhc2ljfQ0KcDwtZ2dwbG90KG15X2RhdGEsIGFlcyhnYSkpKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zPTI1KSsNCiAgZmFjZXRfd3JhcCh+b3VfdHlwZSwgbmNvbD0xKSsNCiAgbGFicyh0aXRsZT0iRXZlbnRzIGJ5IEdBIGFuZCBPVSB0eXBlIikNCnANCg0KYGBgDQoNCg0KIyMgRGVuc2l0eSBQbG90IGJ5IE9yZyBVbml0IHR5cGUgYW5kIEdlc3RhdGlvbmFsIEFnZQ0KDQpBbm90aGVyIHdheSB0byBzaG93IHRoaXMgaGlzdG9ncmFtIGlzIGEgZGVuc2l0eSBkb3QgcGxvdC4gVG8gbWFrZSBpdCBpbnRlcmVzdGluZyB3ZSBjYW4gYW5pbWF0ZSBpdCwgdG8gZW1waGFzaXplIHRoaXMgcHJvZ3Jlc3Npb24gb2YgdGltZS4gSXQgbG9va3MgYSBiaXQgbGlrZSBhIHBhaW50IHJvbGxlci4uLg0KDQpgYGB7ciBkZW5zaXR5IGFuaW19DQoNCnA8LWdncGxvdChteV9kYXRhLCBhZXMoZ2EsIG91X3R5cGUpKSArDQogIGdlb21faml0dGVyKGFlcyhncm91cCA9IGdhLCBzaXplID0gLjMpLCBoZWlnaHQgPSAwLjI1LCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdWaXNpdHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtjbG9zZXN0X3N0YXRlfScsDQogICAgICAgeSA9ICdPcmcgVW5pdCBUeXBlJykgKw0KICMgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjb2xfc2NhbGUpICsNCiAgdHJhbnNpdGlvbl9zdGF0ZXMoZ2EsIHRyYW5zaXRpb25fbGVuZ3RoID0gMywgc3RhdGVfbGVuZ3RoID0gMikgKw0KICBzaGFkb3dfbWFyayhzaXplID0gLjIpICsNCiAgZWFzZV9hZXMoJ2xpbmVhcicpDQoNCmFuaW1hdGUoDQogIHBsb3QgPSBwLCANCiAgbmZyYW1lcyA9IDIwMCwNCiAgZHVyYXRpb24gPSAxNSwNCiAgZW5kX3BhdXNlID0gNTANCikNCg0KDQoNCmBgYA0KDQpCdXQgdGhpcyBkb2VzbnQgc2F5IG11Y2ggYWJvdXQgcGF0aWVudCBtb3ZlbWVudC4gV2hlbiBkbyBjbGllbnRzIG1vdmUgdG8gYSBkaWZmZXJlbnQgb3JnIHVuaXQgY2xpbmljPw0KDQoqIFRoZSByZWQgZG90cyBhcmUgdGhlIEdBIGFuZCBsb2NhdGlvbiAob3JnIHVuaXQpIG9mIHRoZSBmaXJzdCBldmVudCAoaWRlbnRpZmljYXRpb24pLiANCg0KKiBUaGUgYmx1ZSBkb3RzIGFyZSBzdWJzZXF1ZW50IGV2ZW50cyB0aGF0IGFyZSBhdCBzYW1lIGxvY2F0aW9uIGFzIGZpcnN0IGV2ZW50LiANCg0KKiBUaGUgZ3JlZW4gZG90cyBhcmUgbW92ZW1lbnQgdG8gYSBESUZGRVJFTlQgbG9jYXRpb24gdGhhbiB0aGUgaWRlbnRpZmljYXRpb24gb3JnIHVuaXQuDQoNCmBgYHtyIGRlbnNpdHkgd2l0aCBjb2xvcnN9DQpwPC1nZ3Bsb3QobXlfZGF0YSwgYWVzKGdhLCBNb3ZlZF9vdV93cmFwKSkgKw0KICBnZW9tX2ppdHRlcihhZXMoZ3JvdXAgPSBnYSwgY29sb3IgPSBNb3ZlZF9vdV93cmFwKSwgc2l6ZSA9IDAuMDEpICsNCiAgZmFjZXRfd3JhcCh+b3VfdHlwZSwgbmNvbCA9IDEpKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICAgIGxhYnModGl0bGU9IlByZWduYW5jeSBldmVudHMgaW4gZS1SZWcgTWF0bGFiIGJ5IE9yZyBVbml0IikgKw0KICAgIHlsYWIoIiIpDQoNCnANCg0KYGBgDQoNCg0KU2FtZSB0aGluZywgYnV0IGFuaW1hdGVkLi4uDQoNCmBgYHtyIGFuaW1hdGVkIHBhaW50IHJvbGxlcn0NCnA8LXAgKw0KICBsYWJzKHRpdGxlPSJQcmVnbmFuY3kgZXZlbnRzIGluIGUtUmVnIE1hdGxhYiBieSBPcmcgVW5pdCIsDQogICAgICAgc3VidGl0bGUgPSAnVmlzaXRzIGF0IEdlc3RhdGlvbmFsIEFnZSB7Y2xvc2VzdF9zdGF0ZX0nKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB0cmFuc2l0aW9uX3N0YXRlcyhnYSwgdHJhbnNpdGlvbl9sZW5ndGggPSAzLCBzdGF0ZV9sZW5ndGggPSAyKSArDQogIHNoYWRvd19tYXJrKHNpemUgPSAuMykgKw0KICBlYXNlX2FlcygnbGluZWFyJykNCg0KYW5pbWF0ZSgNCiAgcGxvdCA9IHAsIA0KICBuZnJhbWVzID0gMjAwLA0KICBkdXJhdGlvbiA9IDE1LA0KICBlbmRfcGF1c2UgPSA1MA0KKQ0KDQoNCmBgYA0KDQoNCk5vdyB3ZSBjYW4gc2VlIHRoYXQgaWYgYSBjbGllbnQgaXMgbm90IGVucm9sbGVkIGJ5IGFuIEZXQSwgaGVyIGZpcnN0IHRpbWUgdmlzaXRpbmcgaXMgb2Z0ZW4gYWZ0ZXIgMzYgd2Vla3MgKEhvbWUgQU5DIHZpc2l0KS4gQnV0IGlmIHNoZSBzdGFydHMgZnJvbSBhbiBIQSB3YXJkIG9yIEZXQSBVbml0LCBzaGUgbWlnaHQgbW92ZSB0byBGV0MgYmV0d2VlbiAxNCBhbmQgMzUgd2Vla3MuIENvbXBhcmF0aXZlbHkgZmV3ZXIgd29tZW4gY2hvb3NlIHRvIG1vdmUgdG8gQ0MsIGlmIHRoZXkgd2VyZSBpZGVudGlmaWVkIGVsc2V3aGVyZS4NCg0KVGhpcyBhcHByb2FjaCB0ZWxscyB1cyB3aGF0IGtpbmQgb2Ygb3JnIHVuaXQgcHJlZ25hbmNpZXMgR08gdG8sIGJ1dCBub3Qgd2hhdCBraW5kIG9mIG9yZyB1bml0IHRoZXkgQ09NRSBmcm9tLiBXZSB3YW50IHRvIG5hcnJvdyBpbiBvbiB0aGUgcGF0aWVudHMgd2hvIHN0YXJ0IGF0IG9uZSBvcmcgdW5pdCwgYW5kIHJlY2VpdmUgc2VydmljZXMgYXQgYW5vdGhlci4NCg0KDQojIyBGYWNldCBHcmlkIC0gQWxsIEV2ZW50cw0KDQpXZSBjYW4gcmVuZGVyIHRoZSBlbnJvbGxtZW50IGFuZCBldmVudCBvcmcgdW5pdCB0eXBlcyBhcyB0d28gc2VwYXJhdGUgZGltZW5zaW9ucyBpbiBhIGdyaWQsIGFuZCB0aGVuIHNob3cgYSBiYXIgY2hhcnQgb2YgZXZlbnQgb3UgY2F0ZWdvcnkgZm9yIGdlc3RhdGlvbmFsIGFnZSByYW5nZXMuDQoNCmBgYHtyfQ0KDQojV2hhdCBwZXJjZW50IG9mIEV2ZW50IDIrIGFyZSBvdXRzaWRlIG91IG9mIGV2ZW50IDE/DQpteV9kYXRhICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKE1vdmVkX291IT0iRXZlbnQgMSIpICU+JSANCiAgZ3JvdXBfYnkoTW92ZWRfb3UpICU+JSANCiAgdGFsbHkoKSAlPiUgDQogIG11dGF0ZShwZXJjZW50PW4vc3VtKG4pKQ0KDQpmYWNncmlkX2RhdGE8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lDQogICAgbXV0YXRlKGdlc3RhZ2VfZXZlbnQ9Y2FzZV93aGVuKA0KICAgIGdhID49IDAgJiBnYSA8IDEwIH4gIjAtMTAiLA0KICAgIGdhID49IDEwICYgZ2EgPCAxOCB+ICIxMC0xNyIsDQogICAgZ2EgPj0gMTggJiBnYSA8IDI0IH4gIjE4LTIzIiwNCiAgICBnYSA+PSAyNCAmIGdhIDwgMjkgfiAiMjQtMjkiLA0KICAgIGdhID49IDI5ICYgZ2EgPCAzNCB+ICIyOS0zMyIsDQogICAgZ2EgPj0gMzQgJiBnYSA8IDQwIH4gIjM0LTM5IiwNCiAgICAgICAgICAgICAgIGdhID49IDQwIH4gIjQwKyIsDQogICkpICU+JSANCiAgc2VsZWN0KCJldmVudF9vdV90eXBlIj1vdV90eXBlLCBldmVyeXRoaW5nKCkpICU+JSANCiAgbGVmdF9qb2luKG91X25hbWVzLCBieT1jKCJFbnJPcmdVbml0Ij0ib3VfaWQiKSkgJT4lIA0KICByZW5hbWUoImVucm9sIj1vdV90eXBlKSAlPiUgDQogIG11dGF0ZV9pZihpcy5mYWN0b3IsIGFzLmNoYXJhY3RlcikgJT4lIA0KICBncm91cF9ieShlbnJvbCwgZXZlbnRfb3VfdHlwZSwgTW92ZWRfb3UsIGdlc3RhZ2VfZXZlbnQpICU+JSANCiAgdGFsbHkoKSAlPiUgDQogIG5hLm9taXQoKQ0KDQojIGZhY2dyaWRfZGF0YSAlPiUgDQojICAgZmlsdGVyKGVucl9vdV90eXBlPT0iRldDIikNCg0KZmFjZ3JpZF9kYXRhICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX2NvbChhZXMoeD1nZXN0YWdlX2V2ZW50LCB5PW4sIGZpbGw9TW92ZWRfb3UpKSArDQogIGZhY2V0X2dyaWQoZW5yb2wgfiBldmVudF9vdV90eXBlLCBsYWJlbGxlciA9IGxhYmVsX2JvdGgpICsNCiAgbGFicyh0aXRsZT0iVG90YWwgRXZlbnRzIGJ5IEVucm9sbG1lbnQgT1UgYW5kIEV2ZW50IE9VIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb249ImJvdHRvbSIsDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkNCg0KYGBgDQoNClRoZSBncmlkIHNob3dzIHRoZSBsaW5rcyBiZXR3ZWVuIG9yZyB1bml0IG9mIGVucm9sbG1lbnQgYW5kIHN1YnNlcXVlbnQgdmlzaXRzLCBzY2FsZWQgdG8gc2hvdyB0b3RhbCB2aXNpdHMuIFRoaXMgZWZmaWNpZW50bHkgY29tbXVuaWNhdGVzIDUgZGltZW5zaW9ucy4NCg0KDQpIb3dldmVyLCB0aGUgOTIlIG9mIGV2ZW50cyBfYWZ0ZXJfIGVucm9sbG1lbnQgb2NjdXIgYXQgdGhlIHNhbWUgb3JnIHVuaXQgYXMgZW5yb2xsbWVudC4gU28gYSBtYWpvciBkb3duc2lkZSBvZiB0aGlzIHNjYWxlIGlzIHRoYXQgd2UgY2Fubm90IGluZmVyIHRyZW5kcyBvZiBjcm9zc292ZXIgYmV0d2VlbiBvcmcgdW5pdHMgKHRoZSBncmVlbiBkb3RzIGxldmVscykuIA0KDQoNCiMjIEhlYXQgTWFwDQoNCklmIHdlIGZvY3VzIG9uIHRoZSBwYXRpZW50cyB3aG8gbW92ZSB0byBhIG5ldyBsb2NhdGlvbiAodGhlIGdyZWVuIGRvdHMgaW4gZG90IHBsb3QgYWJvdmUpLCB3ZSBjYW4gc2VlIHRoZSBvdmVybGFwIG9mIHNlcnZpY2UgcHJvdmlzaW9uIGJldHdlZW4gdHlwZXMgb2Ygb3JnIHVuaXQuDQoNCkhlcmUgaXMgYSB0YWJsZSBvZiBldmVudHMgdGhhdCBhcmUgYXQgYSAqZGlmZmVyZW50IG9yZyB1bml0KiB0aGFuIHRoZSBlbnJvbGxtZW50IG9yZyB1bml0LCBhcnJhbmdlZCBieSBvcmcgdW5pdCB0eXBlLg0KDQpgYGB7ciBwY29yZHMgaGVhdG1hcCBkYXRhfQ0KcGNvcmRzX2RhdGEgPC1teV9kYXRhICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgZmlsdGVyKE1vdmVkX291PT0iRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikgJT4lIA0KICByZW5hbWUoImV2ZW50X291X2lkIj1vdV9pZCwgImV2ZW50X291X3R5cGUiPW91X3R5cGUpICU+JSANCiAgbGVmdF9qb2luKG91X25hbWVzLCBieT1jKCJFbnJPcmdVbml0Ij0ib3VfaWQiKSkgJT4lIA0KICByZW5hbWUoImVucl9vdV90eXBlIj1vdV90eXBlKSAlPiUNCiAgc2VsZWN0KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlLCAiZXZlbnRfZ2EiPWdhLCAiZW5yX2dhIj1nYV9pbml0aWFsKSANCg0KDQp0ZXN0PC1wY29yZHNfZGF0YSAlPiUNCiAgYXJyYW5nZShldmVudF9vdV90eXBlKSAlPiUgDQogIG11dGF0ZShldmVudF9vdV90eXBlPWZhY3RvcihldmVudF9vdV90eXBlLCBsZXZlbHM9YygiQ0MiLCJGV0MiLCJVbml0IiwiV2FyZCIpKSkgJT4lIA0KICBncm91cF9ieShlbnJfb3VfdHlwZSwgZXZlbnRfb3VfdHlwZSkgJT4lIA0KICBzZWxlY3QoImVucm9sbG1lbnQgT1UiPWVucl9vdV90eXBlLCAiZXZlbnQgT1UiPWV2ZW50X291X3R5cGUpICU+JSANCiAgc3VtbWFyaXplKGNvdW50PW4oKSkNCg0Ka2FibGUodGVzdCkgJT4lDQogIGthYmxlX3N0eWxpbmcoKQ0KDQoNCmBgYA0KDQpXZSBjYW4gdmlzdWFsaXplIHRoaXMgdGFibGUgZ3JhcGhpY2FsbHkgaW4gYSBoZWF0bWFwDQoNCmBgYHtyIGhlYXRtYXAgMX0NCiMgR2l2ZSBleHRyZW1lIGNvbG9yczoNCmxpYnJhcnkodmlyaWRpcykNCg0KZ2dwbG90KHRlc3QsIGFlcyhgZW5yb2xsbWVudCBPVWAsIGBldmVudCBPVWAsIGZpbGw9IGNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RkFMU0UpICsNCiAgdGhlbWVfbWluaW1hbCgpDQoNCmBgYA0KDQpXZSBjYW4gZmFjZXQgdGhlc2UgZG93biBieSBlbnJvbGxtZW50IE9VLCB0aGVuIHNob3cgdGhlIGV2ZW50IEdBIGZvciBlYWNoIHN1YnNlcXVlbnQgZXZlbnQsIGFnYWluIGNsdXN0ZXJpbmcgYnkgZ2VzdGF0aW9uYWwgYWdlLg0KDQpgYGB7ciBoZWF0bWFwIDJ9DQoNCmhlYXQyPC1wY29yZHNfZGF0YSAlPiUNCiAgICBtdXRhdGUoZXZlbnRfb3VfdHlwZT1mYWN0b3IoZXZlbnRfb3VfdHlwZSwgbGV2ZWxzPWMoIkNDIiwiRldDIiwiVW5pdCIsIldhcmQiKSkpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2VfZXZlbnQ9Y2FzZV93aGVuKA0KICAgIGV2ZW50X2dhID49IDAgJiBldmVudF9nYSA8IDE4IH4gIjAtMTciLA0KICAgIGV2ZW50X2dhID49IDE4ICYgZXZlbnRfZ2EgPCAyNCB+ICIxOC0yMyIsDQogICAgZXZlbnRfZ2EgPj0gMjQgJiBldmVudF9nYSA8IDI5IH4gIjI0LTI5IiwNCiAgICBldmVudF9nYSA+PSAyOSAmIGV2ZW50X2dhIDwgMzQgfiAiMjktMzMiLA0KICAgIGV2ZW50X2dhID49IDM0ICYgZXZlbnRfZ2EgPCA0MCB+ICIzNC0zOSIsDQogICAgICAgICAgICAgICAgICAgICBldmVudF9nYSA+PSA0MCB+ICI0MCsiLA0KICApKSAlPiUgDQogIGdyb3VwX2J5KGVucl9vdV90eXBlLCBldmVudF9vdV90eXBlLCBnZXN0YWdlX2V2ZW50KSAlPiUgDQogIHNlbGVjdCgiZW5yb2xsbWVudCBPVSI9ZW5yX291X3R5cGUsICJldmVudCBPVSI9ZXZlbnRfb3VfdHlwZSwgZ2VzdGFnZV9ldmVudCkgJT4lIA0KICBzdW1tYXJpemUoY291bnQ9bigpKQ0KDQoNCmdncGxvdChoZWF0MiwgYWVzKGdlc3RhZ2VfZXZlbnQsIGBldmVudCBPVWAsIGZpbGw9IGNvdW50KSkgKyANCiAgZ2VvbV90aWxlKCkgKw0KICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGU9RkFMU0UpICsNCiAgZmFjZXRfd3JhcCh+YGVucm9sbG1lbnQgT1VgLGxhYmVsbGVyID0gImxhYmVsX2JvdGgiKSsNCiAgbGFicyh0aXRsZT0iRXZlbnRzIGF0IGRpZmZlcmVudCBPVSB0aGFuIGVucm9sbG1lbnQiLA0KICAgICAgIHN1YnRpdGxlPSJCeSBFdmVudCBHZXN0IEFnZSBhbmQgT1UgVHlwZSIpDQoNCg0KYGBgDQoNClRoaXMgaXMgZ29vZCwgYnV0IGZvciBpbnRlcnByZXRhYmlsaXR5LCBjb2xvciBpcyBub3QgYXMgcGVyY2VwdGlibGUgYXMgYXJlYS4gV2UgY2FuIHRha2UgdGhlIHBhbmVsIGdyaWQgZnJvbSBlYXJsaWVyIGFuZCB6ZXJvIGluIG9uIHRoZSBzYW1lIHN1YnNldCBvZiBldmVudHMgYXMgdGhlIGhlYXQgbWFwLg0KDQojIyBGYWNldCBHcmlkIC0gQ3Jvc3NPdmVyDQoNCmBgYHtyfQ0KDQpmYWNncmlkX2RhdGEgJT4lIA0KICBmaWx0ZXIoTW92ZWRfb3UgPT0iRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikgJT4lIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fY29sKGFlcyh4PWdlc3RhZ2VfZXZlbnQsIHk9biksIGZpbGw9ImRhcmtncmVlbiIpICsNCiAgZmFjZXRfZ3JpZChlbnJvbCB+IGV2ZW50X291X3R5cGUsIGxhYmVsbGVyID0gbGFiZWxfYm90aCkgKw0KICBsYWJzKHRpdGxlPSJFdmVudHMgYXQgRGlmZmVyZW50IE9VIHRoYW4gRW5yb2xsbWVudCIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJib3R0b20iLA0KICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpDQoNCg0KYGBgDQoNClRoaXMgbWF5IGJlIHRoZSBtb3N0IGVmZmljaWVudCB3YXkgdG8gcmVwcmVzZW50IG9yZyB1bml0IGNyb3Nzb3ZlciBpbiBNQ0ggY29udGV4dC4gKipUaGUgbW9zdCBjb21tb24gZm9sbG93IHVwIHZpc2l0IHRvIGEgbmV3IG9yZyB1bml0IGFwcGVhcnMgdG8gYmUgYSBVbml0IC0+IEZXQyB0cmFuc2ZlciwgYXJvdW5kIDE4LTIzIHdlZWtzLioqDQoNCg0KIyMgQW5pbWF0ZWQgRG90IFBsb3Qgb2YgVHJhbnNmZXIgRXZlbnRzDQoNCklmIGEgY2xpZW50IG1vdmVzIHRvIGEgZGlmZmVyZW50IGNsaW5pYywgYXQgd2hhdCB0cmltZXN0ZXIgd291bGQgc2hlIHVzdWFsbHkgbWFrZSB0aGF0IGNoYW5nZT8gSXMgaXQgc2hvcnRseSBhZnRlciB0aGUgZmlyc3QgdmlzaXQsIG9yIGlzIHRoZXJlIGEgbG9uZyBkZWxheT8NCg0KVG8gZW1waGFzaXplIHRoZSB0aW1lIGRpbWVuc2lvbiBvZiB0cmFuc2ZlcnMsIHdlIHJldHVybiB0byBhbmltYXRpb25zLiBUaGlzIGFuaW1hdGlvbiBmb2N1c2VzIG9uIHRoZSAiY3Jvc3NvdmVyIiBldmVudHMsIG9yIHRoZSBncmVlbiBkb3RzIGluIHRoZSBkb3QgcGxvdCBhYm92ZS4gVGh1cywgZWFjaCBncmVlbiBkb3QgaXMgYW4gKmV2ZW50Ki4gVGhlIGhvcml6b250YWwgbGluZXMgcmVwcmVzZW50IGEgdHlwZSBvZiBvcmcgdW5pdC4gVGhlIG1pZGRsZSBncmV5IGRvdHMgcmVwcmVzZW50IHRoZSBpZGVudGlmaWNhdGlvbiBldmVudHMtLW9uY2UgdGhleSBjcm9zcyB0aGF0IGRvdCwgdGhlIHByZWduYW5jeSBpcyBpZGVudGlmaWVkLg0KDQpBZnRlciB0aGF0LCBjbGllbnRzIGdvIHRvIG1hbnkgb3RoZXIgdHlwZXMgb2Ygb3JnIHVuaXRzLiBTb21lIGdvIHRvIGEgZGlmZmVyZW50IG9yZyB1bml0IG9mIHNhbWUgdHlwZSwgd2hpbGUgb3RoZXJzIGdvIHRvIGEgbmV3IG9yZyB1bml0IHR5cGUuDQoNCkJ5IGFuaW1hdGluZyB0aGlzIG92ZXIgZ2VzdGF0aW9uYWwgYWdlIGF0IHZpc2l0LCB3ZSBjYW4gc2VlIHdoaWNoIHdlZWtzIGhhZCBoaWdoICJjcm9zc292ZXIiIG9mIGV2ZW50cy4gVGhlIHNwZWVkIG9mIGRvdCBtb3ZlbWVudCByZXByZXNlbnRzIHRoZSB0aW1lIGJldHdlZW4gdmlzaXRzLiANCg0KVGhpcyBhbmltYXRpb24gZXhlbXBsaWZpZXMgdGhlICJTaW1pbGFyaXR5IiBhbmQgICJjb21tb24gZmF0ZSIgR2VzdGFsdCBwcmluY2lwbGU6IGJlY2F1c2UgZWFjaCBkb3QgaXMgdGhlIHNhbWUgY29sb3IgYW5kIHNpemUsIHRoZSBwcmltYXJ5IGluZm9ybWF0aW9uIGNoYW5uZWwgaXMgbW92ZW1lbnQgZnJvbSBvbmUgbGV2ZWwgdG8gYW5vdGhlci4NCg0KYGBge3IgYW5pbWF0aW9ufQ0KDQpwY29yZDI8LXBjb3Jkc19kYXRhICU+JSANCnJvd25hbWVzX3RvX2NvbHVtbih2YXI9ImlkIikgJT4lIA0KbXV0YXRlKCJlbnJfc3RhcnQiPWVucl9vdV90eXBlKSAlPiUgDQpwaXZvdF9sb25nZXIoYygnZW5yX3N0YXJ0JywgJ2Vucl9vdV90eXBlJywnZXZlbnRfb3VfdHlwZScpLCANCiAgICAgICAgICAgICBuYW1lc190byA9ICJzdGFydF9maW5pc2giLCANCiAgICAgICAgICAgICB2YWx1ZXNfdG89Im91X3R5cGUiKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2Uoc3RhcnRfZmluaXNoPT0iZW5yX291X3R5cGUiLCBlbnJfZ2EsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBpZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImV2ZW50X291X3R5cGUiLCBldmVudF9nYSwgMCkpKSAlPiUgDQogIG11dGF0ZShlbmRwb2ludD1pZl9lbHNlKHN0YXJ0X2ZpbmlzaD09ImVucl9zdGFydCIsIDAsDQogICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2Uoc3RhcnRfZmluaXNoPT0iZW5yX291X3R5cGUiLCAxLCAyKSkpICU+JSANCiAgbXV0YXRlKG91X3R5cGU9ZmFjdG9yKG91X3R5cGUsIGxldmVscyA9IGMoIlVuaXQiLCJXYXJkIiwiRldDIiwiQ0MiKSkpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2U9aWZfZWxzZShlbmRwb2ludD09MiAmIGVucl9nYSA+IDMwICYgZ2VzdGFnZSA+IDMwLCBnZXN0YWdlICsgMiwgZ2VzdGFnZSkpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2U9aWZfZWxzZShlbmRwb2ludD09MiwgZ2VzdGFnZSArIDEsIGdlc3RhZ2UpKQ0KDQoNCnBjb3JkX3N1bW08LXBjb3JkMiAlPiUgDQogIGZpbHRlcihlbmRwb2ludCE9MCkgJT4lIA0KICBncm91cF9ieShlbmRwb2ludCwgb3VfdHlwZSwgZ2VzdGFnZSkgJT4lIA0KICBzdW1tYXJpc2UoImNvdW50Ij1uX2Rpc3RpbmN0KGlkKSkgJT4lIA0KICBtdXRhdGUoImN1bXN1bSI9Y3Vtc3VtKGNvdW50KSkNCg0KI3Bjb3JkMiAlPiUgZmlsdGVyKGVuZHBvaW50PT0yICYgZ2VzdGFnZSA+IDM1KQ0KDQoNCnBzMTwtcGNvcmRfc3VtbSAlPiUgZmlsdGVyKGVuZHBvaW50PT0xKQ0KcHMyPC1wY29yZF9zdW1tICU+JSBmaWx0ZXIoZW5kcG9pbnQ9PTIpDQoNCnAyPC1nZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBjb3JkMiwgYWVzKHg9ZW5kcG9pbnQsIHkgPSBvdV90eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBpZCksIGNvbCA9ICJncmVlbiIpICArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBzMSwgYWVzKHg9ZW5kcG9pbnQsIHkgPSBvdV90eXBlLCBzaXplID0gY3Vtc3VtKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPSJncmV5IiwgYWxwaGE9MC44KSArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBzMiwgYWVzKHg9ZW5kcG9pbnQsIHkgPSBvdV90eXBlLCBzaXplID0gY3Vtc3VtKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2w9ImdyZXkiLCBhbHBoYT0wLjgpICsNCiAgdGhlbWVfbWluaW1hbCgpICsgDQogIHRyYW5zaXRpb25fcmV2ZWFsKGdlc3RhZ2UpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKDAsIDEsIDIpLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJHQSAwIiwiSWRlbnRpZmljYXRpb24iLCAiT3RoZXIgRXZlbnQiKSkgKw0KICBsYWJzKHRpdGxlPSJlLVJlZyBNYXRsYWIgLS0gRXZlbnRzIGF0IERpZmZlcmVudCBPcmcgVW5pdCB0aGFuIElkZW50aWZpY2F0aW9uIiwNCiAgICAgICBzdWJ0aXRsZSA9ICdFdmVudHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtyb3VuZChmcmFtZV9hbG9uZyl9JywNCiAgICAgICB4ID0gIiIpDQoNCmFuaW1hdGUocDIsDQogICAgICAgIG5mcmFtZXMgPSAyMDAsDQogICAgICAgIGR1cmF0aW9uID0gMTUsDQogICAgICAgIGVuZF9wYXVzZSA9IDUwKQ0KYGBgDQoNCklmIGEgZG90IG1vdmVzIHF1aWNrbHkgYmV0d2VlbiBpZGVudGlmaWNhdGlvbiBhbmQgc3Vic2VxdWVudCB2aXNpdCwgdGhhdCBtZWFucyB0aGF0IHRoZSBuZXh0IHZpc2l0IGhhcHBlbmVkIHF1aWNrbHkgYWZ0ZXIgaWRlbnRpZmljYXRpb24gKGUuZy4sIFVuaXQgaWRlbnRpZmllcyBwcmVnbmFuY3kgYXQgMTYgd2Vla3MsIGZhY2lsaXR5IHZpc2l0IHRvIENDIGF0IDE4IHdlZWtzKS4gSW52ZXJzZWx5LCB0aGUgc2Vjb25kIHZpc2l0IG1heSBvY2N1ciBsb25nIGFmdGVyIGlkZW50aWZpY2F0aW9uIChpZiB0aGUgc2FtZSBjbGllbnQgYWdhaW4gdmlzaXRzIENDIGF0IDMyIHdlZWtzLCB0aGlzIHdvdWxkIGJlIGEgc2xvd2VyIG1vdmluZyBkb3QpLg0KDQpFdmVudHVhbGx5IHdlIHdpbGwgcmVjcmVhdGUgdGhlc2UgZGF0YSBhcyBhIFtTYW5rZXkgZmxvdyBkaWFncmFtXShodHRwczovL3d3dy5odml0ZmVsZHQubWUvYmxvZy9yZWNyZWF0ZS1zYW5rZXktZmxvdy1jaGFydC8pIGFjcm9zcyA0IEFOQyB2aXNpdHMuIFtTZWUgYmVsb3cuXSgjc2Fua2V5KSANCg0KDQoNCg0KIyMgQ2hvcmQgRGlhZ3JhbQ0KDQpTbyBmYXIsIHRoZXNlIHZpc3VhbGl6YXRpb25zIGhhdmUgZXhhbWluZWQgdGhlIHR5cGUgb2YgdmlzaXQsIGFuZCB3aGV0aGVyIGl0IHdhcyBkaWZmZXJlbnQgdGhhdCB0aGUgbG9jYXRpb24gd2hlcmUgdGhlIHByZWduYW5jeSBlbnJvbGxlZC4gWWV0IG1hbnkgcHJlZ25hbmNpZXMgaGF2ZSBtb3JlIHRoYW4gb25lIHZpc2l0LiBTbyB3ZSBjb3VsZCByZWZyYW1lIHRoZSBxdWVzdGlvbiBvbiBfVklTSVRzXyB0byBhIHF1ZXN0aW9uIGFib3V0IF9wcmVnbmFuY2llc186IHdoYXQgaXMgdGhlIGZyZXF1ZW5jeSBvZiBjby1vY2N1cnJlbmNlIGJldHdlZW4gZGlmZmVyZW50IG9yZyB1bml0IHR5cGVzIHdpdGhpbiBwcmVnbmFuY2llcz8gV2hhdCBpcyB0aGUgcGVyLWNsaWVudCBvdmVybGFwPw0KDQpbQ2hvcmQgZGlhZ3JhbXNdKCBodHRwczovL2Jvc3Qub2Nrcy5vcmcvbWlrZS91YmVyZGF0YS8pIG1pZ2h0IGJlIGhlbHBmdWwgaGVyZS4gQSByZWNlbnQgc3R1ZHkgaGFzIHNob3duIHRoYXQgY2hvcmQgZGlhZ3JhbXMgY2FuIGJlIHVzZWZ1bCBpbiB0aGUgY29udGV4dCBvZiBSTU5DSC4gRGF0YSBmcm9tIEtlbnlhJ3MgREhTIHdlcmUgcmVuZGVyZWQgIGFzIGEgY2hvcmQgZGlhZ3JhbSBpbiBSIHRvIGV4cHJlc3MgdGhlIHllYXItb3Zlci15ZWFyIGNvbnRyYWNlcHRpdmUgdHJhamVjdG9yeSwgb3IgImNodXJuIi4gV2hlbiBwcmVzZW50ZWQgdG8gcGlsb3QgdGVzdGVycyBhdCBhbiBpbnRlcm5hdGlvbmFsIGNvbmZlcmVuY2UgaW4gUndhbmRhLCB0ZXN0ZXJzIG5lZWRlZCBsaW1pdGVkIHN1cHBvcnQgd2l0aCB0aGUgaW50ZXJhY3RpdmUgdmlzdWFsIHRvIHVuZGVyc3RhbmQgdGhlICJmbG93IiBiZXR3ZWVuIGNvbnRyYWNlcHRpb24gdHlwZXMuDQoNClRoZSBiZWxvdyBjaG9yZCBkaWFncmFtIGNhbiBiZSBpbnRlcnByZXRlZCBhcyB0aGUgbnVtYmVyIG9mIHdvbWVuIHdobyB2aXNpdGVkIG1vcmUgdGhhbiBvbmUgb3JnIHVuaXQgZHVyaW5nIHRoZWlyIHByZWduYW5jeSwgYXJyYW5nZWQgYnkgb3JnIHVuaXQgdHlwZS4gRm9yIGV4YW1wbGUsIDMzOCB3b21lbiB2aXNpdGVkIGJvdGggdGhlaXIgRldBIFVuaXQgYW5kIGFuIEZXQy4gU2Nyb2xsIG92ZXIgdGhlIGxpbmtzIHRvIHNlZSBhbGwgY29ubmVjdGlvbnMuIA0KDQoNCg0KDQpgYGB7ciBjaG9yZCBkaWFncmFtfQ0KDQojU1RBUlRFUiBEQVRBDQpzdGFydDwtbXlfZGF0YSAlPiUgDQogICAgc2VsZWN0KHRlaSwgb3VfdHlwZSwgbmFtZSwgVmlzaXRObykgJT4lIA0KICAgIGRpc3RpbmN0KCkgJT4lIA0KICAgIHNlbGVjdCgtVmlzaXRObykNCiN1bmlxdWUgR0Egd2Vla3MvdmlzaXQNCg0KI3NlbGYgam9pbg0Kc3RhcnRlbmQ8LXN0YXJ0ICU+JSANCiAgbGVmdF9qb2luKHN0YXJ0LCBieT1jKCJ0ZWkiKSwgc3VmZml4PWMoIl9zdGFydCIsIl9lbmQiKSkgJT4lIA0KICBmaWx0ZXIobmFtZV9zdGFydCE9bmFtZV9lbmQpICU+JSAjbm90IGEgcmVwZWF0IHZpc2l0L2V2ZW50IGF0IHNhbWUgY2xpbmljDQogIHVuZ3JvdXAoKSAlPiUNCiAgc2VsZWN0KC10ZWksIC1uYW1lX3N0YXJ0LCAtbmFtZV9lbmQpICU+JSANCiAgZ3JvdXBfYnkob3VfdHlwZV9zdGFydCwgb3VfdHlwZV9lbmQpICU+JSAjZ3JvdXAgYnkgb3UgdHlwZSBhbmQgdGFsbHkNCiAgbXV0YXRlKCJjb3VudCI9bigpKSAlPiUgDQogIGRpc3RpbmN0KCkNCg0KICANCiNjcmVhdGUgYSBjby1vY2N1cmVuY2UgbWF0cml4DQpkbTwtYXMubWF0cml4KGlncmFwaDo6YXNfYWRqYWNlbmN5X21hdHJpeChhc190YmxfZ3JhcGgoc3RhcnRlbmQpLGF0dHIgPSAiY291bnQiKSkNCg0KDQojY3JlYXRlIGRpYWdyYW0NCmxpYnJhcnkoY2hvcmRkaWFnKQ0KY2hvcmQ8LWNob3JkZGlhZyhkYXRhID0gZG0sDQojICAgICAgICAgICAgICAgICAgICAgIGdyb3VwQ29sb3JzID0gYygiIzAwMDAwMCIsICIjRkZERDg5IiwgIiM5NTcyNDQiLCAiI0YyNjIyMyIpLA0KICAgICAgICAgICAgICAgICAgICAgIHNob3dHcm91cG5hbWVzID1UUlVFICwNCiAgICAgICAgICAgICAgICAgICAgICBzaG93VGlja3MgPUZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIHRvb2x0aXBHcm91cENvbm5lY3RvciA9ICIgICAgJiN4MjVCNjsgICAgIiwNCiAgICAgICAgICAgICAgICAgICAgICBjaG9yZGVkZ2VDb2xvciA9ICIjQjNCNkI3Ig0KICAgICAgICAgICAgICAgICAgICAgICkNCmNob3JkDQoNCmBgYA0KDQpUaGlua2luZyB3aGF0IHR5cGVzIG9mIGxvY2F0aW9ucyBwcm92aWRlIHNlcnZpY2VzIHRvIHRoZSBzYW1lIHByZWduYW5jeSBpcyBhIGZpcnN0IHN0ZXAgdG8gZGlhZ3JhbW1pbmcgdGhlIGNhc2NhZGUgb2YgY2FyZS4NCg0KIyBNYXBwaW5nIHRoZSBDYXNjYWRlIG9mIENhcmUNCg0KVGhpcyBzZWN0aW9uIHNob3dzIHRoZSAqKlNFUVVFTkNFKiogb2YgdmlzaXRzIGZvciBlYWNoIHBhdGllbnQuIEZvbGxvd2luZyB0aGUgcHJldmlvdXMgYW5pbWF0aW9ucywgd2UgY2FuIHRoaW5rIG9mIHRoZXNlIGFzIGEgY2FzY2FkZSwgZmxvd2luZyBmcm9tIG9uZSBsb2NhdGlvbiB0byB0aGUgbmV4dC4gTm90ZSB0aGF0IHRoZXNlIGRhdGEgd291bGQgY29uc2lkZXJzIGVhY2ggaWRlbnRpZmljYXRpb24sIEFOQyB2aXNpdCwgb3IgaG9tZSB2aXNpdCBzdGFnZSBhdCBhIGRpc3RpbmN0IG9yZyB1bml0cyBvciBHQSBhcyBhIHNlcGFyYXRlICJ2aXNpdCIuIA0KDQojIyBEcm9wb3V0czogSXNvbGF0aW5nIENsaWVudHMgd2l0aCBvbmx5IG9uZSBldmVudA0KDQpBYm91dCAxOSUgb2YgY2xpZW50cyBoYXZlIG9ubHkgb25lIGV2ZW50IGluIHRoZSBzeXN0ZW0uIE1heWJlIHRoZXkgYXJlIHN5c3RlbWF0aWNhbGx5IGRpZmZlcmVudCBmb3Igc29tZSByZWFzb24gdGhhbiBvdGhlciBjbGllbnRzIG9yIGV2ZW50cy4NCg0KRm9yIGV4YW1wbGUsIHdoYXQgc3RhZ2Ugd2FzIHRoZWlyIG9ubHkgZXZlbnQ/DQoNCldoYXQga2luZCBvZiBvcmcgdW5pdD8NCg0KVGhlIGJlbG93IHRhYmxlcyBhcmUgb25seSBvbmUgZXZlbnQuIA0KDQpGaXJzdCBpcyBieSBvcmcgdW5pdCAtLSBtb3N0IG9mIHRoZXNlIGFyZSB0aGUgUHJlZ25hbmN5IElEIHN0YWdlLg0KDQpgYGB7ciBrYWJsZX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCiMjI0lzb2xhdGUgdGhvc2UgdGhhdCBvbmx5IGhhdmQgb25lIGV2ZW50DQpteV9kYXRhX2lzbzwtcmF3NCAlPiUgDQogIGxlZnRfam9pbihvdV9uYW1lcywgYnk9YygiRXZ0T3JnVW5pdCI9Im91X2lkIikpICU+JSANCiAgc2VsZWN0KHRlaT0zLCBnYT04LCAib3VfaWQiPUV2dE9yZ1VuaXQsIG91X3R5cGUsIG5hbWUsIFZpc2l0Tm8sIEVuck9yZ1VuaXQsIFN0YWdlVWlkKSAlPiUNCiAgbGVmdF9qb2luKHBzLCBieSA9IGMoIlN0YWdlVWlkIj0iaWQiKSkgJT4lIA0KICBtdXRhdGUoZ2E9cm91bmQoYXMubnVtZXJpYyhnYSkpKSAlPiUgDQogIGZpbHRlcighaXMubmEob3VfdHlwZSkgJiAhaXMubmEoZ2EpICYgDQogICAgICAgICAgIGdhIDw9IDUwICYgZ2EgPj0gMSAmDQogICAgICAgICAgc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoInJlZ25hbmMiLCAiQU5DIiwgIk5ld2Jvcm4iLCJQTkMiLCJMYWIiKSxjb2xsYXBzZSA9ICd8JykpICYNCiAgICAgICAgICAhc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoIlByZXYiLCJSaXNrIiwiTWFuYWciKSxjb2xsYXBzZSA9ICd8JykpKSAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBhZGRfdGFsbHkoKSAlPiUgDQogIGFycmFuZ2UodGVpLCBnYSkgJT4lIA0KICBtdXRhdGUoZmlyc3QgPSBkcGx5cjo6Zmlyc3Qob3VfaWQpKSAlPiUNCiAgbXV0YXRlKGxhc3Rfb3UgPSBsYWcob3VfaWQpKSAlPiUNCiAgdW5ncm91cCgpICU+JSANCiAgbXV0YXRlKE1vdmVkX291ID0gY2FzZV93aGVuKGZpcnN0ID09IG91X2lkICYgaXMubmEobGFzdF9vdSkgIH4gIkV2ZW50IDEiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgPT0gb3VfaWQgJiAhaXMubmEobGFzdF9vdSkgfiAiRXZlbnQgMissIHNhbWUgb3UgYXMgRXZlbnQgMSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlyc3QgIT0gb3VfaWQgfiAiRXZlbnQgMissIGRpZmZlcmVudCBvdSBhcyBFdmVudCAxIikpDQoNCiMgbXlfZGF0YV9pc28gJT4lDQojICAgZ3JvdXBfYnkobikgJT4lDQojICAgc3VtbWFyaXNlKCJldmVudHMiPSBuKCkpICU+JQ0KIyAgIG11dGF0ZShwZXJjZW50ID0gcm91bmQoZXZlbnRzL3N1bShldmVudHMpLCAyKSkNCg0KDQoNCnRlc3QyPC1teV9kYXRhX2lzbyAlPiUgDQogIGdyb3VwX2J5KHRlaSkgJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShwc25hbWUpICU+JSANCiAgc3VtbWFyaXplKCJzdGFnZV9jb3VudCI9bigpKQ0KDQp0ZXN0MzwtbXlfZGF0YV9pc28gJT4lIA0KICBmaWx0ZXIobj09MSkgJT4lIA0KICBncm91cF9ieShvdV90eXBlKSAlPiUgDQogIHN1bW1hcml6ZSgib3VfdHlwZV9jb3VudCI9bigpKQ0KDQoNCiNrYWJsZUV4dHJhOjprYWJsZSh0ZXN0MikNCmthYmxlKHRlc3QyKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQoNCmBgYA0KDQpOZXh0IGlzIGJ5IG9yZyB1bml0LiBNb3N0IG9mIHRoZXNlIGFyZSB0aGUgRldBIFVuaXRzLg0KDQpgYGB7cn0NCmthYmxlKHRlc3QzKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoKQ0KDQpgYGANCg0KDQoNCiMjIFNhbmtleSBEaWFncmFtDQoNCkEgdHJhZGl0aW9uYWwgd2F5IHRvIGRpc3BsYXkgZmxvdyBvZiByZXNvdXJjZXMgb3IgcGVvcGxlIGlzIHRocm91Z2ggYSBTYW5rZXkgRGlhZ3JhbS4gVGhlc2UgY2FuIGdyb3cgZnJvbSBbc2ltcGxlXShodHRwczovL3d3dy5kYXRhLXRvLXZpei5jb20vZ3JhcGgvc2Fua2V5Lmh0bWwpIHRvIHF1aXRlIFt0ZWNobmljYWwgYW5kIGNvbXBsZXhdKGh0dHBzOi8vd3d3LmllYS5vcmcvc2Fua2V5LykuDQoNCkJlbG93IHdlIHNob3cgdGhlIHNjYWxlIG9mIG1vdmVtZW50IGJldHdlZW4gdmlzaXRzIGF0IGRpZmZlcmVudCBsb2NhdGlvbnMuIEl0IGJlZ2lucyB3aXRoICJBTEwiIHByZWduYW5jaWVzIG9uIHRoZSBmYXIgbGVmdCwgYW5kIGVuZHMgd2l0aCAiTFRGVSIgKExvc3QgVG8gRm9sbG93IFVwKSBvbiB0aGUgZmFyIHJpZ2h0LiBPcmdhbml6YXRpb24gdW5pdCB0eXBlcyBhcmUgbWF0Y2hlZCBieSBjb2xvciwgYW5kIHZpc2l0IG51bWJlciBjb3JyZXNwb25kIHRvIHRoZSBYIGF4aXMuDQoNCkludGVyYWN0aXZpdHksIHByb3ZpZGVkIGJ5IHRoZSBbIm5ldHdvcmtEMyIgcGFja2FnZV0oaHR0cHM6Ly9jaHJpc3RvcGhlcmdhbmRydWQuZ2l0aHViLmlvL25ldHdvcmtEMy8jc2Fua2V5KSwgaGVscHMgdG8gZHJpbGwgZG93biB0byB0aGUgc2VnbWVudGVkIGZsb3dzLiBTY3JvbGwgb3ZlciBhICJub2RlIiAob3JnIHVuaXQgdHlwZSkgdG8gc2hvdyBhbGwgcGF0aWVudHMgYXNzb2NpYXRlZCB3aXRoIGl0LiBTY3JvbGwgb3ZlciBhICJsaW5rIiB0byBzZWUgdG90YWwgbnVtYmVyIG9mIGNsaWVudHMgd2hvIG1vdmVkIGJldHdlZW4gdGhlc2Ugb3JnIHVuaXRzIGF0IHRoZSBjb3JyZXNwb25kaW5nIHZpc2l0IG51bWJlci4NCg0KYGBge3Igc2Fua2V5fQ0KbGlicmFyeShuZXR3b3JrRDMpDQoNCmNhc2NhZGVfZGF0YSA8LW15X2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICByZW5hbWUoImV2ZW50X291X2lkIj1vdV9pZCwgImV2ZW50X291X3R5cGUiPW91X3R5cGUpICU+JSANCiAgc2VsZWN0KHRlaSwgZXZlbnRfb3VfdHlwZSwgImV2ZW50X2dhIj1nYSwgTW92ZWRfb3UsIFN0YWdlVWlkLCBldmVudF9vdV9pZCkgJT4lIA0KICBsZWZ0X2pvaW4ocHMsIGJ5ID0gYygiU3RhZ2VVaWQiPSJpZCIpKSAlPiUgDQogIGZpbHRlcighaXMubmEoZXZlbnRfb3VfdHlwZSkgJiAhaXMubmEoZXZlbnRfZ2EpICYgDQogICAgICAgICAgIGV2ZW50X2dhIDw9IDUwICYgZXZlbnRfZ2EgPj0gMSAmDQogICAgICAgICAgc3RyX2RldGVjdChwc25hbWUsIHBhc3RlKGMoInJlZ25hbmMiLCAiQU5DIiksY29sbGFwc2UgPSAnfCcpKSAmDQogICAgICAgICAgIXN0cl9kZXRlY3QocHNuYW1lLCBwYXN0ZShjKCJQcmV2IiwiUmlzayIsIk1hbmFnIiwgIk91dCIpLGNvbGxhcHNlID0gJ3wnKSkpICU+JSANCiAgZ3JvdXBfYnkodGVpKSAlPiUgDQogIGRpc3RpbmN0KHRlaSwgZXZlbnRfZ2EsIGV2ZW50X291X2lkLCAua2VlcF9hbGwgPSBUUlVFKSAlPiUgDQojICBtdXRhdGUoIlZpc2l0Tm8iPWlmX2Vsc2Uoc3RyX2RldGVjdChwc25hbWUsICJpZGVudCIpLCAxLCAyKSkgJT4lIA0KICBhcnJhbmdlKGV2ZW50X2dhKSAlPiUgDQogIG11dGF0ZSgiVmlzaXRObyI9cm93X251bWJlcigpKSANCg0KDQpjYXNjYWRlX2RhdGE8LWNhc2NhZGVfZGF0YSAlPiUgDQogIG11dGF0ZSgiVmlzaXRObyI9aWZfZWxzZShWaXNpdE5vPT0xLCAwLCBhcy5kb3VibGUoVmlzaXRObykpKSAlPiUgDQogIGJpbmRfcm93cyhjYXNjYWRlX2RhdGEgJT4lICBmaWx0ZXIoVmlzaXRObyA9PSAxKSkgJT4lIA0KICBtdXRhdGUoIk1vdmVkX291Ij1pZl9lbHNlKFZpc2l0Tm8gPD0xLCAiRXZlbnQgMSIsIE1vdmVkX291KSkgJT4lIA0KICBtdXRhdGUoZXZlbnRfb3VfdHlwZT1mYWN0b3IoZXZlbnRfb3VfdHlwZSwgbGV2ZWxzID0gYygiVW5pdCIsIldhcmQiLCJGV0MiLCJDQyIpKSkgJT4lIA0KICBtdXRhdGUoZ3JvdXBpZD0gZ3JvdXBfaW5kaWNlcygpKSAlPiUgDQogIGFycmFuZ2UodGVpLCBWaXNpdE5vKSAlPiUgDQogIG11dGF0ZShnZXN0YWdlPWlmX2Vsc2UoVmlzaXRObz09MCwgMCwgZXZlbnRfZ2EpKSAlPiUgDQogIGZpbHRlcihWaXNpdE5vPDgpICU+JSANCiAgbXV0YXRlKGdlc3RhZ2U9aWZfZWxzZShnZXN0YWdlPT1sYWcoZ2VzdGFnZSkgJiBncm91cGlkPT1sYWcoZ3JvdXBpZCkgJiBnZXN0YWdlIT0wLCBsYWcoZ2VzdGFnZSkrMiwgZ2VzdGFnZSkpDQoNCg0KDQpzYW5rPC1jYXNjYWRlX2RhdGEgJT4lIA0KICB1bmdyb3VwKCkgJT4lIA0KICBzZWxlY3QoZ3JvdXBpZCwgVmlzaXRObywgZXZlbnRfb3VfdHlwZSkgJT4lIA0KICBncm91cF9ieShncm91cGlkKSAlPiUgDQogIG11dGF0ZShldjFfdHlwZT1pZl9lbHNlKFZpc2l0Tm89PTAsICJBTEwiLCBhcy5jaGFyYWN0ZXIoZXZlbnRfb3VfdHlwZSkpLA0KICAgICAgICAgZXYyX3R5cGU9aWZfZWxzZSghaXMubmEobGVhZChldjFfdHlwZSkpLCBsZWFkKGV2MV90eXBlKSwgIkxURlUiKSkgJT4lIA0KICBtdXRhdGUoc291cmNlID0gcGFzdGUwKGV2MV90eXBlLCAnXycsIFZpc2l0Tm8pKSAlPiUNCiAgbXV0YXRlKHRhcmdldCA9IHBhc3RlMChldjJfdHlwZSwgJ18nLCBWaXNpdE5vKzEpKSAlPiUNCiAgbXV0YXRlKHRhcmdldD1pZl9lbHNlKHN0cl9kZXRlY3QodGFyZ2V0LCAiTFRGVSIpLCAiTFRGVSIsIHRhcmdldCkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgc2VsZWN0KHNvdXJjZSwgdGFyZ2V0KSANCg0KDQpub2RlcyA8LSBkYXRhLmZyYW1lKG5hbWUgPSB1bmlxdWUoYyhzYW5rJHNvdXJjZSwgc2FuayR0YXJnZXQpKSkNCg0KbGlua3M8LXNhbmsgJT4lIA0KICBncm91cF9ieV9hbGwoKSAlPiUgDQogIHRhbGx5KG5hbWUgPSAidmFsdWUiKQ0KDQpsaW5rcyRzb3VyY2UgPC0gbWF0Y2gobGlua3Mkc291cmNlLCBub2RlcyRuYW1lKSAtIDENCmxpbmtzJHRhcmdldCA8LSBtYXRjaChsaW5rcyR0YXJnZXQsIG5vZGVzJG5hbWUpIC0gMQ0KDQoNCm5vZGVzJG5hbWUgPC0gc3ViKCdfWzAtOV0rJCcsICcnLCBub2RlcyRuYW1lKQ0KDQoNCnNhbmtleU5ldHdvcmsoTGlua3MgPSBsaW5rcywgTm9kZXMgPSBub2RlcywgU291cmNlID0gJ3NvdXJjZScsDQogICAgICAgICAgICAgIFRhcmdldCA9ICd0YXJnZXQnLCBWYWx1ZSA9ICd2YWx1ZScsIE5vZGVJRCA9ICduYW1lJykNCg0KDQpgYGANCg0KDQojIyBBbmltYXRlZCBDYXNjYWRlIGJ5IFZpc2l0IE51bWJlcg0KDQpXaGlsZSB0aGUgU2Fua2V5IGRpYWdyYW0gZG9lcyBhIGdvb2Qgam9iIG9mIHNob3dpbmcgX3NjYWxlXyBvZiBmbG93LCBpdCBtYWtlcyBsaXR0bGUgdXNlIG9mIHRoZSBfdGltZV8gYmV0d2VlbiB2aXNpdHMuIEF0IHdoYXQgZ2VzdGF0aW9uYWwgYWdlIHRvIHRyYW5zZmVycyBiZXR3ZWVuIHVuaXRzIGFuZCBGV0Mgb2NjdXI/DQoNCldlIGNhbiB2aXN1YWxpemUgdGhlIHZpc2l0IG51bWJlciAoeCBheGlzKSwgR0EgYXQgdmlzaXQgKHRpbWUpLCBhbmQgdHlwZSBvZiBvcmcgdW5pdCBhdCBlYWNoIHZpc2l0ICh5IGF4aXMpLiBUaGUgaW5zcGlyYXRpb24gaXMgdGhpcyBbTllUaW1lcyBpbmZvZ3JhcGhpY10oaHR0cHM6Ly93d3cubnl0aW1lcy5jb20vaW50ZXJhY3RpdmUvMjAxOC8wMy8xOS91cHNob3QvcmFjZS1jbGFzcy13aGl0ZS1hbmQtYmxhY2stbWVuLmh0bWwpDQoNCkVhY2ggZG90IHJlcHJlc2VudHMgYSBzaW5nbGUgcGF0aWVudCBhcyBzaGUgbW92ZXMgdGhyb3VnaCBlYWNoIGxldmVsIG9mIHRoZSBNQ0ggc3lzdGVtLiBUaGUgcGF0aWVudCdzIGRvdCAicmVzdHMiIGF0IHRoZSBsb2NhdGlvbiBvZiBoZXIgbGFzdCB2aXNpdCByZWNvcmRlZC4gDQoNCllvdSBjYW4gc2VlIHRoYXQgdmVyeSBmZXcgcGF0aWVudHMgZ2V0IHBhc3QgdGhlIDNyZCB2aXNpdCBhdCBhbnkgbGV2ZWwuIE1vc3Qgb2YgdGhlIG1pZ3JhdGlvbiBvY2N1cnMgZnJvbSB0aGUgRldBIFVuaXQgbGV2ZWwgInVwIiB0aGUgc3lzdGVtIHRvIEZXQy4gQW5kIGNvbXBhcmVkIHRvIENDIGxldmVsLCBtb3JlIGNsaWVudHMgd2hvIG1hZGUgaXQgdG8gRldDIGJ5IHRoZSB0aGlyZCB2aXNpdCBzdGFydGVkIGZyb20gYW5vdGhlciBvcmcgdW5pdCAocmVkIGRvdHMpLg0KDQoNCmBgYHtyIGNhc2NhZGUgYnkgdmlzaXR9DQoNCnAzPC1nZ3Bsb3QoKSArDQogIGdlb21faml0dGVyKGRhdGEgPSBjYXNjYWRlX2RhdGEsIGFlcyh4PVZpc2l0Tm8sIHkgPSBldmVudF9vdV90eXBlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cGlkLCBjb2wgPSBNb3ZlZF9vdSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplPTAuNSwgd2lkdGggPSAwLjE1LCBoZWlnaHQ9MC4xKSAgKw0KICB0aGVtZV9taW5pbWFsKCkgKyANCiAgdHJhbnNpdGlvbl9yZXZlYWwoZ2VzdGFnZSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPWMoMDo3KSwNCiAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiR0EgMCIsICJWaXNpdDEiLCAiVmlzaXQyIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVmlzaXQzIiwiVmlzaXQ0IiwiVmlzaXQ1IiwgIlZpc2l0NiIsIlZpc2l0NyIpLA0KICAgICAgICAgICAgICAgICAgIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsNCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5IiwgInJlZCIsICJkYXJrYmx1ZSIpKSArDQogIGxhYnModGl0bGU9ImUtUmVnIE1hdGxhYiAtLSBFdmVudHMgMS03IiwNCiAgICAgICBzdWJ0aXRsZSA9ICdFdmVudHMgYXQgR2VzdGF0aW9uYWwgQWdlIHtyb3VuZChmcmFtZV9hbG9uZyl9JywNCiAgICAgICB4ID0gIiIsDQogICAgICAgeT0iIikrDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQ0KDQoNCmFuaW1hdGUocDMsDQogICAgICAgIG5mcmFtZXMgPSAyMDAsDQogICAgICAgIGR1cmF0aW9uID0gMTUsDQogICAgICAgIGVuZF9wYXVzZSA9IDUwKQ0KDQojZ2dhbmltYXRlOjphbmltX3NhdmUoImJkX3Zpc2l0MXRvNy5naWYiKQ0KDQoNCiNvbmx5IHR3byBURUkgd2VudCB0byBtb3JlIHRoYW4gMiBvcmcgdW5pdHMhIQ0KIyBjYXNjYWRlX2RhdGEgJT4lIA0KIyAgIGdyb3VwX2J5KHRlaSkgJT4lIA0KIyAgIG11dGF0ZShvdXM9bl9kaXN0aW5jdChldmVudF9vdV9pZCkpICU+JSANCiMgICBmaWx0ZXIob3VzPjIpICU+JSANCiMgICBhcnJhbmdlKHRlaSkNCg0KIyBjYXNjYWRlX2RhdGEgJT4lDQojICAgZ3JvdXBfYnkodGVpKSAlPiUNCiMgICBmaWx0ZXIoZ2VzdGFnZT4wKSAlPiUgDQojICAgbXV0YXRlKG91cz1uX2Rpc3RpbmN0KGV2ZW50X291X2lkKSwNCiMgICAgICAgICAgcmFuZ2U9bWF4KGdlc3RhZ2UpLW1pbihnZXN0YWdlKSwNCiMgICAgICAgICAgbWF4dj1tYXgoVmlzaXRObykpICU+JQ0KIyAgIGZpbHRlcihvdXM+MSwgbWF4dj40LCByYW5nZT4xMCwgZXZlbnRfb3VfdHlwZT09IlVuaXQiKSAlPiUgDQojICAgIGFycmFuZ2UodGVpKQ0KDQpgYGANCg0KDQoNCg0KIyMjIENhc2NhZGUgLS0gSGlnaGxpZ2h0aW5nIGFuIEluZGl2aWR1YWwgQ2FzZQ0KDQpJdCdzIHZlcnkgcmFyZSB0aGF0IGEgcGF0aWVudCBtYWtlcyBtb3JlIHRoYW4gb25lIHRyYW5zZmVyIGJldHdlZW4gb3JnYW5pemF0aW9uIHVuaXRzLS1vbmx5IHR3byBURUkgd2VudCB0byB0aHJlZSBvciBtb3JlIG9yZyB1bml0cy4gQnV0IHdoZW4gc3VjaCB0cmFuc2ZlcnMgZG8gaGFwcGVuLCB3ZSBjYW4gaGlnaGxpZ2h0IHRoaXMgY2FzZSB0byBwdXQgaXQgaW4gY29udGV4dC4NCg0KV2Ugc2VlIHRoYXQgdGhpcyBjYXNlIG1vdmVzIGZyb20gRldBIFVuaXQgdG8gQ0MgZm9yIEFOQyAxLCBiYWNrIHRvIFVuaXQsIHRoZW4gdHdvIG1vcmUgdmlzaXRzIGF0IENDIGluIHJhcGlkIHN1Y2Nlc3Npb24uDQoNCg0KYGBge3J9DQojbGV0J3MgZm9sbG93IHRoZSBwYXRoIG9mIG9uZSANCmNkMjwtY2FzY2FkZV9kYXRhICU+JSANCiAgbXV0YXRlKCJzaXplIj1pZl9lbHNlKHRlaT09IldMZ2JtZUt1RVhJIiwgMSwgMC41KSkNCg0KDQogY2QzPC1jZDIgJT4lDQogICBmaWx0ZXIodGVpPT0iV0xnYm1lS3VFWEkiKQ0KDQoNCiNyZWNyZWF0ZSBpdCB3aXRoIGEgbGFyZ2VyIGRvdA0KcDQ8LWdncGxvdCgpICsNCiAgZ2VvbV9qaXR0ZXIoZGF0YSA9IGNkMiwgYWVzKHg9VmlzaXRObywgeSA9IGV2ZW50X291X3R5cGUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXBpZCwgc2l6ZT1zaXplLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBNb3ZlZF9vdSwgZ3JvdXA9Z3JvdXBpZCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoID0gMC4xNSwgaGVpZ2h0PTAuMSwgYWxwaGE9MC41KSAgKw0KICBnZW9tX3NlZ21lbnQoZGF0YSA9IGNkMywgYWVzKHg9VmlzaXRObywgeGVuZD1WaXNpdE5vLCB5PTAsIHllbmQ9ZXZlbnRfb3VfdHlwZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cGlkKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJkYXJrZ3JlZW4iLCBzaXplPTAuOCwgYWxwaGE9MC42KSArDQogIGdlb21fdGV4dChkYXRhID0gY2QzLCBhZXMoeD1WaXNpdE5vLCB5PTAuOCwgbGFiZWw9Il9IaWdobGlnaHRlZCBDYXNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBncm91cGlkKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yPSJkYXJrZ3JlZW4iLCBzaXplPTUsIGhqdXN0PTAsIGFscGhhPTAuNikgKw0KICB0aGVtZV9taW5pbWFsKCkgKw0KICB0cmFuc2l0aW9uX3JldmVhbChnZXN0YWdlKSArDQogIHNjYWxlX3NpemVfY29udGludW91cyhndWlkZT1OVUxMKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9YygwOjcpLA0KICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJHQSAwIiwgIlZpc2l0MSIsICJWaXNpdDIiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICJWaXNpdDMiLCJWaXNpdDQiLCJWaXNpdDUiLCAiVmlzaXQ2IiwiVmlzaXQ3IiksDQogICAgICAgICAgICAgICAgICAgbWlub3JfYnJlYWtzID0gTlVMTCkgKw0KICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcyA9IGMoImdyZXkiLCAicmVkIiwgImRhcmtibHVlIikpICsNCiAgbGFicyh0aXRsZT0iZS1SZWcgTWF0bGFiIC0tIFZpc2l0cyAxLTciLA0KICAgICAgIHN1YnRpdGxlID0gJ1Zpc2l0cyBhdCBHZXN0YXRpb25hbCBBZ2Uge3JvdW5kKGZyYW1lX2Fsb25nKX0nLA0KICAgICAgIHggPSAiIiwNCiAgICAgICB5PSIiKSsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpDQoNCiNwNA0KYW5pbWF0ZShwNCwNCiAgICAgICAgbmZyYW1lcyA9IDIwMCwNCiAgICAgICAgZHVyYXRpb24gPSAxNSwNCiAgICAgICAgZW5kX3BhdXNlID0gNTApDQoNCg0KYGBgDQoNCg0KDQojIFN1bW1hcnkgYW5kIERpc2N1c3Npb24NCg0KVGhpcyBwYXBlciBoYXMgZXhwbG9yZWQgc29tZSBtb2RhbGl0aWVzIHRvIGRpc3BsYXkgbXVsdGktZGltZW5zaW9uYWwgb3JnVW5pdCBjcm9zc292ZXIgZGF0YSBmcm9tIERISVMyIFRyYWNrZXIuIFRoZSBlUmVnaXN0cmllcyBwcm9qZWN0IGluIE1hdGxhYiwgQmFuZ2xhZGVzaCBvZmZlcmVkIGEgcHJpbWUgb3Bwb3J0dW5pdHkgdG8gZXhwbG9yZSB2aXN1YWxpemF0aW9ucyBvZiBwYXRpZW50IG1pZ3JhdGlvbiBiZWhhdmlvciwgc2luY2UgYW4gYWR2YW5jZWQgYmlvbWV0cmljIHNvbHV0aW9uIGVuYWJsZWQgYSBzaGFyZWQgcGF0aWVudCByZWNvcmQgdGhhdCB3YXMgYWNjZXNzaWJsZSB0byBjb21tdW5pdHkgc2VydmljZSBwcm92aWRlcnMgYW5kIGNsaW5pY2FsIHN0YWZmLiBBcyBtb3JlIGhlYWx0aCBzeXN0ZW1zIHdvcmsgd2l0aCBjb21tb24gcGF0aWVudCByZWdpc3RlcnMsIHN1Y2ggYW5hbHlzZXMgb2YgcGF0aWVudCBtaWdyYXRpb24gZmxvd3Mgd2lsbCBncm93IG1vcmUgcHJldmFsZW50LCBtb3JlIHNvcGhpc3RpY2F0ZWQsIGFuZCBtb3JlIGNvbXBsZXguIE9wZW4gc291cmNlIGNsaWVudCB0cmFja2luZyBzb2Z0d2FyZSBzdWNoIGFzIERISVMyIHNob3VsZCBwcm9tb3RlIHN0YW5kYXJkIHZpc3VhbGl6YXRpb24gZnJhbWV3b3JrcyB0byBoZWxwIGhlYWx0aCBzeXN0ZW0gbWFuYWdlcnMgbWFrZSBzZW5zZSBvZiB0aGVzZSBwYXR0ZXJucy4NCg0KSW5pdGlhbCANCg0Kfn5+IEluc2VydCB0YWJsZSBzaG93aW5nIGFuYWx5c2lzIG9mIGNoYXJ0IHR5cGUgYnkgdmlzdWFsIGNoYW5uZWwgaGVyZQ0KDQpgYGB7cn0NCg0KYGBgDQoNCg0KDQo=